BGM警告 前方高能
接续上一期的所有权的学习,所有权的内容中强调的是变量是资源的所有者,拥有对资源的控制权(例如移动,释放),但并不是所有的变量都拥有所指向的资源,那就是引用(Reference)
这次来了解Rust中的引用该咋用和需要注意的规则。
一、引用
定义不可变引用(关注定义,不关注代码是否有意义)下列两个定义是相同的:
let is_a_reference = &5; // 对5的引用; &i32类型
let ref other_reference = 5; //对于5 的引用;&i32类型
& 符号在赋值时要用在值的前面,ref用在变量的前面。
可变引用定义:
let is_a_reference = &mut 5; // 对5的引用; &i32类型
let ref mut other_reference = 5; //对于5 的引用; &i32类型
代表的是对引用的数据的修改权,修改时则需要 解引用(dereference)
*is_a_reference = 25;
*other_reference = 10;
引用规则:同一资源可以存在多个不可变引用
let num_saved_in_stack = 5;
let immutable_ref1 = &num_saved_in_stack;
let immutable_ref2 = &num_saved_in_stack;
let immutable_ref3 = &num_saved_in_stack;
引用规则:同一资源存在引用时,该资源在引用释放前或使用之前不可被移动或释放。
fn move_value(_s: String) {
println!("s will drop in the fn");
}
fn main() {
let ownership_value = String::from("test");
let ref str_ref = ownership_value;
println!("{}", str_ref);
//所有权移动到方法内。
move_value(ownership_value);
//? 注释掉下方行则会消除错误。
println!("{}", str_ref);
}
引用规则:同一资源一次只能存在一个可变引用,同一作用域内不可存在不可变引用。
let mut ownership_value = String::from("test");
//编译不通过
let ref mut str_ref1 = ownership_value; //第一个可变引用
let ref mut str_ref2 = ownership_value; //第二个可变引用
// 编译失败 同时存在了两个可变引用
println!("{}", str_ref1);
println!("{}", str_ref2);
let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value; //第一个可变引用
{
let ref mut str_ref2 = ownership_value;//第二个可变引用
println!("{}", str_ref2);
}
//编译失败,引用使用前引用第二次。
//? 注释掉下方行则会消除错误。
println!("{}", str_ref1); //第一个可变引用在此处使用
let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value; //第一个可变引用
let ref str_ref12 = ownership_value;
//编译失败,同一作用域内存在可变和不可变引用。
println!("{}", str_ref1);
println!("{}", str_ref12);
下方代码是可以编译的:
let mut ownership_value = String::from("test");
{
let ref mut str_ref2 = ownership_value;
println!("{}", str_ref2);
}
//此时第一个可变借用已经释放,
let ref mut str_ref1 = ownership_value;
println!("{}", str_ref1);
如下代码是可以编译的。
let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value;
println!("{}", str_ref1);
let ref mut str_ref2 = ownership_value;
println!("{}", str_ref2);
总结这一规则:在引用声明和引用使用的中间不能包含对该资源对第二次引用,规则中的“一次”也就是指这个意思,一次引用可以考虑为,从声明开始到最后一次使用结束。
引用规则:资源存在使用的引用时,在当前作用域中这一资源是不可被修改的。称之为“冻结”。其实与上一个规则有些相似。
let mut a = Box::new(5);
let b = &a;
a = Box::new(6);
// 以下编译时会发生错误。
println!("{}", b);
println!("{}", a);
二、借用
借用是与引用密不可分的,当把引用用作方法的参数,则称之为借用(borrow)。
Rust的编译器内存在一个借用检查器,检查的就是上一小节的引用规则。
借用使用场景:当方法不需要获取输入参数的所有权,则需要使用借用。如下例子中borrow_fn并不需要获取n的所有权,仅仅使用值进行判断。
fn borrow_fn(n: Box<i32>) {
//解Box引用
if *n > 2 {
println!("gather than 2");
} else {
println!("less than or equal 2")
}
}
fn main() {
let num = Box::new(5);
// ? 发生所有权转移;
borrow_fn(num);
// ?注释掉可消除错误,err:使用了已经移动的变量。
println!("{}", num);
}
所以需要使用借用,可以这样用:
fn borrow_fn(n: &Box<i32>) {
//解Box引用,需要解两次引用
if **n > 2 {
println!("gather than 2");
} else {
println!("less than or equal 2")
}
}
fn main() {
let num = Box::new(5);
// ? 发生借用;
borrow_fn(&num);
// 借用不拥有原始资源,无权释放,后面可以继续使用。
println!("{}", num);
}
也可以这样用:
fn borrow_fn(n: &i32) {
//解引用
if *n > 2 {
println!("gather than 2");
} else {
println!("less than or equal 2")
}
}
fn main() {
let num = Box::new(5);
// ? 发生借用,Box的自动解引用起作用
borrow_fn(&num);
println!("{}", num); // 这里的打印实际就是自动解引用。
}
以上是不可变借用。
可变借用(同样遵守引用规则):
fn mut_borrow_value(n: &mut i32) {
*n = *n * 2;
}
fn main() {
let mut num = Box::new(5);
// ? 发生可变借用;原始值需要是可变的。
mut_borrow_value(&mut num);
println!("{}", num);
}
借用时,可变引用可以作为不可变引用参数,反之则不行:
fn mut_borrow_value(n: &i32) {
if *n > 2 {
println!("gather than 2");
} else {
println!("less than or equal 2")
}
}
fn main() {
let mut num = Box::new(5);
// ? 发生可变借用;原始值需要是可变的。
mut_borrow_value(&mut num);
println!("{}", num);
}
三、解引用与解构
解引用需要使用 * ,上面的例子中也已经展示过了。
而需要获取解构的引用时则情况不太一样,可以使用ref获取解构内成员的不可变引用和可变引用:
struct Rect {
w: i32,
h: i32,
}
fn main() {
let r = Rect{w:1, h:1};
let Rect{w: ref ww, h} = r;
println!("{}, {}", ww, h);
}
传说
有一个瞎编古老的传说:很久很久以前,唐老大与唐老二、唐老三、唐老四被江湖人称唐氏四兄弟。传说他们有一台32寸大彩电,不过电视只归老大所有,其余三兄弟由于没有遥控器,只有乖乖看的份,不能随意换节目。
唐家家规很严,对电视节目的播放非常严格,管家每次都会监视他们:
正是唐家家规的这么严格,造就了唐家人铁一样的坚毅性格,而且从来没有因为抢遥控器看节目发生过争执,后来在动漫江湖中有了很大的成就。
你学会瞎编引用借用了吗?