素数|连续 3 年最受欢迎:Rust,香!( 二 )


obj是String对象的所有权变量。
2)值或对象有且只能有一个所有者。
3)当所有者离开作用域,所有者所代表的对象或者值会被立即销毁。
4)赋值语句、函数调用、函数返回等会导致所有权转移,原有变量会失效。
例如:
fn main() { let s = String::from("hello"); let s1 = s; //所有权发生了转移,由s转移给s1 print!("{}",s); //s无效,不能访问,此句编译会报错}
fn test(s1:String){ print!("{}",s1);}
fn main() { let s = String::from("hello"); test(s); //传参,所有权发生了转移 print!("{}",s); //此处s无效,编译报错}
Rust的所有权规则保证了同一时刻永远只有一个变量持有一个对象的所有权,避免数据竞争。
2借用规则
可能大家都发现了问题,什么鬼,为什么我传了个参数s给test函数,这参数s后面还不能用了呢?如果我接下来要使用变量s怎么办?这时候就要用到Rust的借用特性。在Rust中,你拥有一个变量的所有权,如果想让其它变量或者函数访问,你可以把它“借”给其它变量或者你所调用的函数,供它们访问。Rust会在编译时检查所有借出的值,确保它们的寿命不会超过值本身的寿命。
例如,以下的写法就没有问题:
fn test(s1:&String;){ print!("{}",s1);}
fn main() { let s = String::from("hello"); test(&s;); //传参,注意只是传递了引用,所有权还归属于s print!("{}",s); //此处s依然有效,可以访问}
fn main() { let s = String::from("hello"); let s1 = &s; //s1借用s,所有权还归属于s print!("{}",s); //此处s依然有效,可以访问 print!("{}",s1); //此处s1和s指向同一个对象}
如果我们尝试修改借用的变量呢?
fn main() { let s = String::from("hello"); change(&s;);
}
fn change(some_string: &String;) { some_string.push_str(", world");}
借用默认是不可变的,上面的代码编译时会报错:
error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable --> error.rs:8:5 |7 | fn change(some_string: &String;) { | ------- use `&mut; String` here to make mutable8 | some_string.push_str(", world"); | ^^^^^^^^^^^ cannot borrow as mutable
根据编译错误的提示,通过mut关键字将默认借用修改为可变借用就OK,如下代码可以编译通过:
fn main() { let mut s = String::from("hello"); change(&mut; s);
}
fn change(some_string: &mut; String) { some_string.push_str(", world");}
不过可变引用有一个很大的限制:在特定作用域中的特定数据有且只能有一个可变引用,这个限制的好处是 Rust 可以在编译时就避免数据竞争,这些代码会失败:
【 素数|连续 3 年最受欢迎:Rust,香!】let mut s = String::from("hello");let r1 = &mut; s;let r2 = &mut; s;
报错如下:
error[E0499]: cannot borrow `s` as mutable more than once at a time --> borrow_twice.rs:5:19 |4 | let r1 = &mut; s; | - first mutable borrow occurs here5 | let r2 = &mut; s; | ^ second mutable borrow occurs here6 | } | - first borrow ends here
在存在指针的语言中,容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针(dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者或者已经被释放。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当我们拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。
让我们尝试创建一个悬垂引用,Rust 会通过一个编译时错误来避免:
fn main() { let reference_to_nothing = dangle();
}
fn dangle() -> &String; { let s = String::from("hello"); &s;}
这里是编译错误:
error[E0106]: missing lifetime specifier --> dangle.rs:5:16 |5 | fn dangle() -> &String; { | ^ expected lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from = help: consider giving it a 'static lifetime
让我们简要的概括一下之前对引用的讨论,以下3条规则在编译时就会检查,违反任何一条,编译报错并给出提示。
1)在任意给定时间,只能 拥有如下中的一个:
一个可变引用。
任意数量的不可变引用。
2)引用必须总是有效的。
3)引用的寿命不会超过值本身的寿命。
3变量生命周期规则
生命周期检查的主要目标是避免悬垂引用,考虑以下示例 中的程序,它有一个外部作用域和一个内部作用域,外部作用域声明了一个没有初值的变量 r,而内部作用域声明了一个初值为 5 的变量 x。在内部作用域中,我们尝试将 r 的值设置为一个 x 的引用。接着在内部作用域结束后,尝试打印出 r 的值:


推荐阅读