Skip to content

所有权

所有权规则

1.Rust 中的每一个值都有一个 所有者(owner)。
2.值在任一时刻有且只有一个所有者。
3.当所有者(变量)离开作用域,这个值将被丢弃。

变量作用域

rust
fn main{
     {                      // s 在这里无效,它尚未声明
        let s = "hello";   // 从此处起,s 是有效的
        // 使用 s
     } // 此作用域已结束,s 不再有效 Rust 在结尾的 } 处自动调用 drop 释放内存
    let s = String::from("hello");
     s.push_str(", world!"); 
     println!("{s}"); // 将打印 `hello, world!`
     let s2 = s;
     //let s2 = s1.clone();//可以访问s1
    println!("{s}"); // 报错,无法访问s,因为它移动到了s2
}

所有权与函数

rust
fn main() {
    let s = String::from("hello");  // s 进入作用域
    takes_ownership(s);             // s 的值移动到函数里 ...
                                    // ... 所以到这里不再有效
    let x = 5;                      // x 进入作用域
    makes_copy(x);                  // x 应该移动函数里,
                                    // 但 i32 是 Copy 的,
                                    // 所以在后面可继续使用 x
} // 这里,x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
  // 没有特殊之处

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{some_string}");
} // 这里,some_string 移出作用域并调用 `drop` 方法。
  // 占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{some_integer}");
} // 这里,some_integer 移出作用域。没有特殊之处

返回值与作用域

rust
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 {             // gives_ownership 会将
                                             // 返回值移动给
                                             // 调用它的函数
    let some_string = String::from("yours"); // some_string 进入作用域。
    some_string                              // 返回 some_string 
                                             // 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
    a_string  // 返回 a_string 并移出给调用的函数
}

引用可借用

引用像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同,引用确保指向某个特定类型的有效值

rust
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);//&s1创建一个指向s1的引用,但是并不拥有它。因为并不拥有这个值,所以当引用停止使用时,它所指向的值也不会被丢弃。
    println!("The length of '{s1}' is {len}.");
}
fn calculate_length(s: &String) -> usize {
    s.len()
}

可变引用 (引用默认是不可以修改的)

rust
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    //let r1 = &mut s; //报错,可变引用有一个很大的限制:如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。
    //let r2 = &mut s; //这个限制的好处是 Rust 可以在编译时就避免数据竞争
    /*
    	//数据竞争
    	1.两个或更多指针同时访问同一数据。
		2.至少有一个指针被用来写入数据。
		3.没有同步数据访问的机制。
		数据竞争会导致未定义行为,难以在运行时追踪,并且难以诊断和修复;Rust 避免了这种情况的发生,因为它甚至不会编译存在数据竞争的代码!
    */
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
//创建新的作用域
fn main(){
    let mut s = String::from("hello");
    {
        let r1 = &mut s;
    } // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
    let r2 = &mut s;
    //不能在拥有不可变引用的同时拥有可变引用。
      let mut s = String::from("hello");
   	  let r1 = &s; // 没问题
      let r2 = &s; // 没问题
      let r3 = &mut s; // 大问题
    // 解决
    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    println!("{r1} and {r2}");
    // 此位置之后 r1 和 r2 不再使用
    let r3 = &mut s; // 没问题
    println!("{r3}");
}

悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域

rust
fn main() {
    let reference_to_nothing = dangle(); //报错
}
fn dangle() -> &String { // dangle 返回一个字符串的引用
    let s = String::from("hello"); // s 是一个新字符串
    &s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。
  // 危险!

引用规则

  • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
  • 引用必须总是有效的。