「可以有任意多个 &T」这条规则背后的原因是:只读不会互相干扰。这篇从三个角度验证它:引用本身是 Copy、多线程并发读真的能跑出正确结果、以及一旦引入「共享 + 修改」(Cell),编译器马上用 Sync 把它拦住。
源码:
1 | fn main() {
2 | let v = vec![1, 2, 3];
3 | let a = &v; let b = &v; let c = a; // 复制一个引用
4 | println!("a、b、c 指向同一个 Vec:{}", std::ptr::eq(a, b) && std::ptr::eq(b, c));
5 | println!("len:{} {} {}", a.len(), b.len(), c.len());
6 | fn is_copy<T: Copy>(_: &T) {}
7 | is_copy(&a);
8 | println!("&Vec<i32> 是 Copy(上面的 is_copy 调用编译通过)");
9 | }
编译:成功
运行(退出码 0):
a、b、c 指向同一个 Vec:true
len:3 3 3
&Vec<i32> 是 Copy(上面的 is_copy 调用编译通过) 源码:
1 | use std::thread;
2 | fn main() {
3 | let data: Vec<u64> = (1..=1_000_000).collect();
4 | let chunks: Vec<&[u64]> = data.chunks(250_000).collect();
5 | let total: u64 = thread::scope(|s| {
6 | let hs: Vec<_> = chunks.iter().map(|c| s.spawn(move || c.iter().sum::<u64>())).collect();
7 | hs.into_iter().map(|h| h.join().unwrap()).sum()
8 | });
9 | println!("4 个线程各读 25 万个元素,共享同一个 Vec,总和 = {}(期望 {})", total, 1_000_000u64 * 1_000_001 / 2);
10 | }
编译:成功
运行(退出码 0):
4 个线程各读 25 万个元素,共享同一个 Vec,总和 = 500000500000(期望 500000500000)Send 与 Sync 的定义见Send 与 Sync。
源码:
1 | use std::cell::Cell;
2 | use std::thread;
3 | fn main() {
4 | let c = Cell::new(0);
5 | thread::scope(|s| {
6 | s.spawn(|| c.set(c.get() + 1));
7 | s.spawn(|| c.set(c.get() + 1));
8 | });
9 | }
编译:失败
error[E0277]: `Cell<i32>` cannot be shared between threads safely
--> <实验目录>/src/u20c.rs:6:17
|
6 | s.spawn(|| c.set(c.get() + 1));
| ----- ^^^^^^^^^^^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
= note: required for `&Cell<i32>` to implement `Send`
note: required because it's used within this closure
--> <实验目录>/src/u20c.rs:6:17
|
6 | s.spawn(|| c.set(c.get() + 1));
| ^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> <rust 标准库源码>/library/std/src/thread/scoped.rs:203:28
|
201 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
202 | where
203 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.需要「共享 + 修改」时用 Mutex、RwLock 或原子类型,见Mutex 与 RwLock。
源码:
1 | fn main() {
2 | let mut v = vec![1, 2, 3];
3 | let r1 = &v; let r2 = &v;
4 | v.push(4);
5 | println!("{:?} {:?}", r1, r2);
6 | }
编译:失败
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> <实验目录>/src/u20d.rs:4:5
|
3 | let r1 = &v; let r2 = &v;
| -- immutable borrow occurs here
4 | v.push(4);
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{:?} {:?}", r1, r2);
| -- immutable borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0502`.