作者回复: 非常赞!
作者回复: 非常棒! 第二题也可以先使用,后 push。 ```rust fn main() { let mut arr = vec![1, 2, 3]; // cache the last item let last = arr.last(); // consume previously stored last item println!("last: {:?}", last); arr.push(4); } ```
作者回复: 非常正确!
作者回复: 你可以用 Box::leak / Box:into_raw / ManuallyDrop 让堆内存完全脱离自动管理。按照你的需求,你可以使用 ManuallyDrop。代码如下: ```rust use std::mem::ManuallyDrop; fn main() { // 使用 ManuallyDrop 封装数据结构使其不进行自动 drop let mut s = ManuallyDrop::new(String::from("Hello World!")); // ManuallyDrop 使用了 Deref trait 指向 T,所以可以当 String 使用 s.truncate(5); println!("s: {:?}", s); // 如果没有这句,s 不会在 scope 结束时被自动 drop(你可以注掉试一下) // 如果我们想让它可以自动 drop,可以用 into_inner let _: String = ManuallyDrop::into_inner(s); } ``` 更详细的代码可以看 playground(我实现了个 MyString,在 Drop trait 中加了打印,这样可以更清楚地看到 drop 是否被调用): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4362bb2f45d252b01822d9206b988019 至于 Box::leak / Box::into_raw,我们后续会慢慢讲到。
作者回复: 好问题。这涉及到 reborrow。在函数调用时,sum(b) 实际上等价于 sum(&mut *b)。reborrow 以后我们看有没有机会和 NLL (non-lexical lifetime) 加餐一起介绍一下。目前我们先理解好基本的所有权/借用/生命周期规则。reborrow / NLL 是为了让代码更简单易写而做的改进。 你可以通过下面的代码看 reborrow 和普通借用的区别: ```rust fn main() { let mut x = 42; let r1 = &mut x; // reborrow 可以通过 let r2 = &*r1; // &x 不可以 // let r2 = &x; println!("r1: {:p}, r2: {:p}", &r1, &r2); *r1 += 1; } ```
作者回复: 👍
作者回复: 嗯,这样也可以。这里隐含着使用了 i32 的 Copy trait,让 last 拷贝了一份。它对 Vec<String> 就不适用,因为 String 会做 move。你可以试着在 playground 运行这端代码看看出什么错误: ```rust fn main() { let mut arr = vec![String::from("a"), String::from("b")]; // cache the last item let last = *arr.last().unwrap(); arr.push(String::from("c")); // consume previously stored last item println!("last: {:?}", last); } ``` 还有其它解法,可以参考我之前的回答。
作者回复: 非常棒!
作者回复: 正确
作者回复: 嗯,「活跃的」这个定语是 Rust 编译器做的一个优化,可以让我们不用添加不必要的作用域。可以简单这么认为:在撰写代码的时候,如果你在某处使用了一个可变引用之后就再也没用了,那么这处之后的地方这个可变引用就不是活跃的了。