Rust 引用 && 借用
继续学rust
今天这篇文章主要跟随上一篇 Rust中的move而写的 引用和借用
主要内容
这里主要讲 rust 中的引用与借用
,根据其中一些例子,尽量扯明白其中的关系和原理,讲的不对的留言,我及时更改。
一、引用是什么?
在rust 中的引用是什么,引用有什么用。这是我们下面的主题之一。
先上代码
let r1 = String::from("hello world");// 注意这里没有mut
let r2 = &r1;// 注意这里没有mut
这个
&
很神奇, 这样给 r2 赋值以后 不会产生 rust中的move
操作
这里表示的就是引用,它们允许你使用值但不获取其所有权
let r1_addr = &r1 as *const String as usize;
let r2_addr = r2 as *const String as usize;
println!("r1 value = {} address is 0x{}",r1,r1_addr);
println!("r2 value = {} address is 0x{}",r2,r2_addr);
这里输出一下上面 r1 和 r2 的地址你会发现他们的地址是一致的
这就证明他们指向了同一块内存空间。
这里我们解释一下----“不获得其所有权”
就是我们r2 引用了 r1 的值 但是没有权限改变这片空间的值。
这里我们修改一下代码
let r1 = String::from("hello world");// 注意这里没有mut
let mut r2 = &r1;// 注意这里的mut
let tmp_str = String::from(", peace and love");
r2 = &tmp_str;
let r1_addr = &r1 as *const String as usize;
let r2_addr = r2 as *const String as usize;
我们发现r2 的值可以正常更改,r2 已经指向了 tmp_str这个空间。
思考
那如果在被 r1 在被 r2 引用期间 r1 改变了值怎么办,或者说根据rust的设计原则, r1 允许被更改吗 ?????
错误代码示范,好孩子不要学
let mut r1 = String::from("hello world"); // 注意这里的mut
let mut r2 = &r1; // 注意这里的mut
let tmp_str = String::from(", peace and love");
r1 = tmp_str; // 肯定爆红的 错了哦这里
let r1_addr = &r1 as *const String as usize;
let r2_addr = r2 as *const String as usize;
我们看看提示的错误:
r1 = tmp_str;
^^ assignment to borrowed
r1occurs here
这里提示: r1 这玩意已经被借走了.
所以不能改呀。
如果你把 r2 相关的代码注释掉肯定是可以的。或者如果r2 的作用域结束了也是可以的,这里大家可以设计一下这个代码自己实现,这里就不写了哦。
二、rust借用
将获取引用作为函数参数称为 借用
既然是借来的,肯定不能给人改了。
借来的东西你给人家改了,还回去时,还能还吗,所以是不允许修改的。
fn main() {
let r1 = String::from("hello world ");
change(&s);
}
fn change(r2: &String) {
some_string.push_str(", peace and love");
}
fn change(r2: &String) {
------- use&mut String
here to make mutable
r2.push_str(", world");
^^ cannot borrow as mutable
不允许修改引用的值
这里也体现了rust的设计原则之一:
- 同一时间只有一个操作对同一片内存空间,要防止数据竞争
三、可变引用
其实在第一节里我已经用到了可变引用,这个没啥难以理解的。
总结一下就是
- 加个 mut 不就可以了么。
- 在特定作用域中的特定数据只能有一个可变引用。
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
自己检查错误吧,别总看,手动起来敲一下 ,加深印象看看报了什么错。
rust 的设计机制,关于数据多引用,涉及数据竞争的,在大部分情况下,代码编写的时候就会跟你提示错误,都不用编译。
四、悬垂引用
在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针(dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域
上面是rust程序语言设计里的解释,巴拉巴拉巴拉一大堆,
乍一看,这啥玩意啊。
鄙人的理解: 一个变量"B"被"C"引用了,这时"B"的生命周期结束,会被drop掉,"C"就无效了,完了你还想"A"去接着这个"C"。 就是这玩意。
不过好在要是发生这种情况,在你写代码时编译器就告诉你错了。除非你没安装插件提示。
fn main() {
let A = dangle(); // A 去找 B了 ,找啥找
}
fn dangle() -> &String {
let B = String::from("hello");
&B // 引用,B离开dangle会被drop掉,所有权没了引用也就失效了
}
这里铁定报错 expected lifetime parameter
总结
让我们概括一下之前对引用的讨论:
- 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用。
- 引用必须总是有效的。
参考文章:https://doc.rust-lang.org/book/ch05-01-defining-structs.html