一、对结构体本身的引用
先定义一个简单的结构体:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
在上一篇学习中,有一个关键点,就是如果一个变量被借用以后,那么在借用结束前,该变量的值都不可改变(即使其为mut类型),这样的好处就是避免了数据竞争。同样,这点对于复合类型,如struct,也是一样。下面代码不可编译:
fn main() {
let mut a = Point { x: 5, y: 5 };
let b = &a;
a.x += 10; // Error! cannot assign to `a.x` because it is borrowed
println!("{:?}", a);
println!("{:?}", b);
}
同样,一个变量被可变借用结束之前,自身不可使用。不可变借用则无此限制。下面代码可以编译:
let mut a = Point { x: 5, y: 5 };
let b = &a;
println!("{:?}", a);
println!("{:?}", b);
以下代码不可编译:
let mut a = Point { x: 5, y: 5 };
let b = &mut a;
println!("{:?}", a);
println!("{:?}", b);
调整上面最后两行的顺序,则编译通过。
可变借用,可以改变内部成员的值。如下:
let mut a = Point { x: 5, y: 5 };
let b = &mut a;
b.x += 10; // (*b).x += 10;
println!("{:?}", b);
println!("{:?}", a);
以上输出a 和 b的值相同:
Point { x: 15, y: 5 }
Point { x: 15, y: 5 }
其中b.x 相当于 (*b).x,编译器进行了隐式解引用。
二、对结构体成员的引用(或指向其成员的指针)
上面都是指向结构体本身的指针,那么能否指向其中的成员呢?又有什么约束和限制?先试一下:
let a = Point { x: 5, y: 5 };
let b = &a.x;
println!("{:?}", a);
println!("{:?}", b);
可以!再试试可变引用:
let mut a = Point { x: 5, y: 5 };
let b = &mut a.x;
*b += 10;
println!("{:?}", b);
println!("{:?}", a);
输出:
15
Point { x: 15, y: 5 }
也可以!但多加一行代码,如注释行,就会编译报错,仍然是为了避免数据污染。因为b可能会改变a.x的值。
let mut a = Point { x: 5, y: 5 };
let b = &mut a.x;
*b += 10;
let c = a.x; // Error! cannot use `a.x` because it was mutably borrowed
println!("{:?}", b);
这里面有一个很有意思的点,分别可变借用x 和 y的值会怎么样?允许吗?下面代码:
let mut a = Point { x: 5, y: 5 };
let b = &mut a.x;
*b += 10;
let c = &mut a.y; // at now b is still active. But you can borrow a.y.
*c += 20;
println!("{:?}", c);
println!("{:?}", b);
println!("{:?}", a);
竟然可以编译和运行!也就是说,对结构体成员的可变引用,是作用在其成员本身的,对一个成员的可变引用,不影响对其它成员的同时的可变引用!
如下代码,有一个所有权的隐藏问题:
let a = Point { x: 5, y: 5 };
let c = a.x; // copy a.x to c
println!("{:?}", a);
println!("{:?}", c);
因为a.x为i32类型,该类型实现了Copy Trait,所以将a.x的值copy到了c。但如果 x为未实现Copy Trait的类型呢?这个下次再讲。
三、Vec类型的引用
深入尝试了struct类型的引用,那么Vec类型如何?以下代码可以运行:
let mut a = vec![1, 2, 3];
let b = &mut a;
b[0] += 10; // (*b)[0] += 10; // It also works!
println!("{:?}", b);
println!("{:?}", a);
输出:
[11, 2, 3]
[11, 2, 3]
也可以可变引用vec成员的值:
let mut a = vec![1, 2, 3];
let b = &mut a[1];
*b += 10;
// let c = &a[2]; // cannot borrow `a` as immutable because it is also borrowed as mutable
println!("{:?}", b);
println!("{:?}", a);
输出:
12
[1, 12, 3]
虽然b 为a[1]的可变引用,可以通过*b修改a[1]的值,但对引用规则的约束,是作用在a上的。否则被注释掉的那一行就会报错。这点与struct类型的成员引用约束是不同的!这估计与rust内部的实现及内存模型有关,有机会去看看rust的源代码才行。