上一篇博客说到,移动是转交所有权,而克隆(Copy和Clone)是获得一个和旧值相同的新值的所有权)。
那么如果我们想不转交所有权又对变量的值进行读取和修改(比如方法的传参问题,会改变所有权,即移动;或者读取和修改的不是原变量的值,即克隆),应该怎么做?
RUST提供了引用和借用的机制。常规引用是一个指针类型,指向了对象存储的内存地址(和C++的指针比较像);获取变量的引用,则叫做借用。
引用允许我们获得对值的使用权,但不是所有权。RUST中使用&获得变量的引用。
C++需要通过*来访问指针指向的值,类似的,RUST也需要使用*来解引用,访问引用指向的值。不过RUST提供了自动解引用功能,大部分时候不需要显式解引用(一些情况如方法传参需要非引用的形参时还是要解引用的)。
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("({}, {})", p.x, p.y); //输出(10, 20)
let q=&p;
println!("({}, {})", (*q).x, (*q).y); //输出(10, 20)
println!("({}, {})", q.x, q.y); //输出(10, 20)
}
刚才说,引用允许获得对变量的使用权。
fn main() {
let x = 5;
let y=&x; //y是x的引用
println!("y: {}", y); //输出y: 5
println!("x: {}", x); //输出x: 5
}
但是上述代码中y只能读取x的值,而不能修改x的值,只实现了我们目标的一半。这又得说到不可变引用和可变引用的概念。
不可变引用,即只能读取变量的值,不能对值进行修改(相当于C++中的const T*)。上面的例子中y就是一个不可变引用。
可变引用,即既能读取变量的值,又能对值进行修改。
fn main() {
let mut x = 5;
let y=&mut x;
(*y)+=1;
println!("y: {}", y); //输出y: 6
println!("x: {}", x); //输出x: 6
}
上述代码中y是x的一个可变引用。使用&mut获取变量的可变引用。需要注意的是,被获取可变引用的变量必须是可变(mut)的。
看到这里大家可能会想,这和C++的指针有啥区别吗?
其实还是有的。在RUST中,引用有两条比较重要的限制:
1. 在相同作用域,同一个变量的可变引用只能存在一个。
2. 相同变量的可变引用和不可变引用不能同时存在。
为什么要有这两条限制呢?原因是可以使Rust在编译期避免数据竞争。而数据竞争会导致未定义行为,这种行为很可能超出我们的预期,难以在运行时追踪,并且难以诊断和修复。
先看第一条限制。
fn main() {
//会报错,y1和y2的作用域重叠了
let mut x = 5;
let y1=&mut x;
let y2=&mut x;
println!("y1: {}", y1);//y1的作用域一直到这里结束
println!("y2: {}", y2); //y2的作用域一直到这里结束
}
再看第二条限制。
fn main() {
let mut x = 5;
let y1= &x;
let y2=&x;
let y3=&mut x; //会报错,因为已经有不可变引用了
println!("y1: {}", y1); //y1作用域到这里结束
println!("y2: {}", y2); //y2作用域到这里结束
println!("y3: {}", y3); ////y3作用域到这里结束
}
再说说引用的作用域。引用的作用域从引用的定义开始,到引用的最后一次使用结束。
fn main() {
let mut x = 5;
let y1= &x; //y1的作用域在这里就结束了
let y2=&x; //y2的作用域在这里就结束了
let y3=&mut x; //不会报错
}
在上述例子中,x的可变引用和不可变引用不会同时存在,所以不会报错。
标签:作用域,y1,let,引用,借用,println,y2,RUST From: https://www.cnblogs.com/nomore007/p/18447842