作者回复: 这个世界本就不纯粹,没有绝对的完美。所有的语言都会撕开一道口子,Haskell 也需要 IO Monad 和外界打交道,Erlang VM 也得忍着可能 crash VM 的风险引入 NIF (native function) 等等。不光语言如此,框架也如此,给你解决 80% 问题的利器,也保留让你解决剩下 20% 问题的灵活度。这还只是一个 Box:leak/Box::into_raw/ManuallyDrop,Rust 还有 unsafe 呢...别着急下结论,让子弹飞一会。
作者回复: 谢谢你这么有深度的思考! 编译器是一个不断进化的过程,在基本的规则下,它可以尽可能多地把常见的,并且合法的情况处理掉,不给开发者太多的负担。所以可以引用引入了是否活跃的概念,以及 NLL(https://rust-lang.github.io/rfcs/2094-nll.html)。 对于文中的内部可变性的例子,检查完全有赖正在执行的函数来确保。的确,对于这个例子,在编译期完全有可能把原本在作用域结束时 drop 的 v 提前到 v 使用完的地方: ```rust use std::cell::RefCell; fn main() { let data = RefCell::new(1); // 获得 RefCell 内部数据的可变借用 let mut v = data.borrow_mut(); *v += 1; // 提前 drop 可变引用 drop(v); println!("data: {:?}", data.borrow()); // 隐含的 drop // 原本 drop(v) 发生在这里 drop(data); } ``` 这样可以避免额外的作用域。 但是,如果这样做,编译器就是在为某个特定的数据结构 RefCell,而非 Rust 的语法单元 &mut 来做优化了。从系统设计的角度,除非能找到一种很通用的方法(比如设计一个新的像 Send/Sync/Unpin 这样的 auto trait 对所有实现了这个 auto trait 的数据结构进行优化),否则,编译器需要知道哪个数据结构在做哪个操作的时候需要特殊处理。这就会让编译器本身的设计变得复杂。 个人意见,不代表编译器团队的思路 :)
作者回复: 赞!非常棒的回答。第 3 题其实不用看代码也可以尝试猜一下:当一个只读引用可以修改内部数据时,它一定是用了内部可变性。:) 一个实际使用的系统一定是符合二八定律的:可以用少量规则来满足 80%的应用场景。但总还有例外需要处理。所以编译期尽管能解决绝大多数的应用场景,但是解决不了的时候,还是需要运行期的检查来弥补。 这就涉及到如何权衡了。Rust 的选择是最小权限原则,你只有显式地要求(比如数据结构用 Arc<T> 封装),才会进行额外的处理(维护引用计数)。其实这也是我们撰写软件系统时应该遵循的原则。 谈到滥用,最小权限原则恰恰是为了防止滥用。比如 mut,当你实际不需要的时候,编译器会报警。Rc/Arc 不必要的 clone,clippy(Rust 的 linting 工具) 会提示。 如果「滥用」智能指针,并不会导致内存安全无法保证,比如你在不需要的时候使用 Arc,损失的是 atomic compare_and_swap 时的性能,但不会带来安全问题。
作者回复: 👍
作者回复: 这就是运行期检查和编译期检查的区别。data.borrow_mut() 产生的 v 会一直活跃到作用域结束,而对于 &mut 编译器可以检查它是否活跃从而让我们撰写代码时不需要额外的作用域。你可以看看我和另一个同学关于这个问题的详细讨论。
作者回复: 好问题。strong 为 0 时,ptr::drop_in_place 已经把大部分内存释放。但此时可以还有孤悬的 weak reference,它们还是可用的,但无法 upgrade。可以看这个代码: ```rust use std::rc::{Rc, Weak}; #[derive(Debug)] struct Foo { bar: &'static str, } impl Drop for Foo { fn drop(&mut self) { println!("dropped!"); } } fn main() { let foo = Rc::new(Foo { bar: "hello" }); let weak_foo = Rc::downgrade(&foo); let other_weak_foo = Weak::clone(&weak_foo); drop(weak_foo); // 不会 drop drop(foo); // 打印 dropped! // 此时还有 weak ref 存在 println!("other weak foo: {:?}", other_weak_foo); // 无法 upgrade assert!(other_weak_foo.upgrade().is_none()); } ``` playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ffd75a38877cfd78b6c66d0b13dc1d90
作者回复: 👍
作者回复: 1、2 正确!3 跟 unsafe 关系不大,跟 strong 是 Cell<usize> 实现了内部可变性有关。(当然,内部可变性底层的实现是 unsafe)
作者回复: 👍