Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

学习自 rust圣经

强烈建议:https://gcc.godbolt.org/ 看反汇编
注意,下文的“新建栈上对象”,其实不是“申请”,而是机器代码控制,在函数体对应的机器代码之前,栈顶就保留了足够长的栈,然后栈底+offset是栈上对象的起始地址

rust所有权规则

  • 每一个值(内存对象)都被一个变量所拥有,该变量被称为值的所有者
  • 一个值(内存对象)同时(同一个作用域内)只能被一个变量所拥有,或者说一个值只能拥有一个所有者
  • 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
    • 具体来说,栈上内存是编译器控制释放的(机器代码栈顶栈底),堆上内存等其他资源是类型的drop方法控制实现的,离开作用域时编译器产生调用drop的汇编代码

let底层

3个let典型例子

  • 全在栈上的对象
fn main() {
    let x = 5; // 栈上新建内存对象,其中内容放5(mov a 5这样子的)
    let y = x; // 栈上新建内存对象,其中内容是复制x绑定的栈上内存对象的内容
}
  • 栈上内存+堆上内存
fn main() {
    // String类型如下图所示
    let s1 = String::from("hello"); // 栈上新建内存对象,内容是指向堆的指针和长度;堆上新建内存对象,内容是hello的utf-8编码的字节数组
    let s2 = s1; // 栈上新建内存对象,内容是s1栈上内存对象的拷贝
    // (这样的话自然指针是指向原先堆上的那个数组的)
    // s1之后不能被借用了
    println!("{}, world!", s1); // 会报错
}
  • 引用:全在栈上的对象
fn main() {
    // 引用就是一个栈上对象,是一个指针,如下图所示
    let x: &str = "hello, world"; 
    let y = x;
    println!("{},{}",x,y);
}

图示最左边的方块是&String类型

所以总结let:新建栈上对象(拷贝右值的栈上对象,或字面值(字面值会编码到机器指令中)),
如果右值对象有堆上对象,且没有实现Copy trait,则新对象的堆上对象就是右值对象的堆上对象(毕竟栈上对象是直接拷贝右值的,指针值还是指向原先的堆上对象),而右值对象会被编译器强制失效,不能再使用了

函数参数和返回值

传递参数,跟let语句一样

fn main() {
    let s = String::from("hello");  // 新建堆栈对象
    takes_ownership(s);             // 看函数的注释
    // s失效
    
    let x = 5;   // 新建栈对象                   
    makes_copy(x); // 看函数的注释 
    // x仍然有效                
}

fn takes_ownership(some_string: String) { // 新建栈对象,是拷贝传递来的参数的栈对象;堆对象是参数的堆对象,传递来的参数被失效
    println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。堆内存被释放

fn makes_copy(some_integer: i32) { // 新建栈对象。由于i32是copy的,因此传递进来的参数不会被失效
    println!("{}", some_integer);
} 

返回值:把返回值的栈上部分放寄存器

fn main() {
    let s1 = gives_ownership();         // gives_ownership 将返回值
                                        // 移给 s1

    let s2 = String::from("hello");     // s2 进入作用域
    
    let s3 = takes_and_gives_back(s2);  // s2 被移动到
                                        // takes_and_gives_back 中,
                                        // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
  // 所以什么也不会发生。s1 移出作用域并被丢弃

fn gives_ownership() -> String {             
    let some_string = String::from("hello"); 
    some_string                              // 返回 some_string 并移出给调用的函数
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
    a_string  // 返回 a_string 并移出给调用的函数
}

引用的作用域

引用的作用域 s 从创建开始,一直持续到它最后一次使用的地方,这个跟变量的作用域有所不同,变量的作用域从创建持续到某一个花括号 }

fn main() {
let mut s = String::from("hello");

let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// 新编译器中,r1,r2作用域在这里结束

let r3 = &mut s; // 新编译器中,这样的代码不违反 同一时间不能同时有可变和不变引用
println!("{}", r3);
} // 老编译器中,r1、r2、r3作用域在这里结束
// 新编译器中,r3作用域在这里结束

评论