在这段代码中:
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
}
impl Post {
pub fn new() -> Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
pub fn content(&self) -> &str {
""
}
pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}
}
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
}
struct Draft {}
impl State for Draft {
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview {})
}
}
struct PendingReview {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
}
Run Code Online (Sandbox Code Playgroud)
有一个电话take();这本书说:
要使用旧状态, request_review 方法需要获取状态值的所有权。这就是 Post 的 state 字段中的 Option 的用武之地:我们调用 take 方法从 state 字段中取出 Some 值,并在其位置留下一个 None 。
我们需要暂时将 state 设置为 None 而不是直接用代码设置它,比如
self.state = self.state.request_review();获取 state 值的所有权。这确保 Post 在我们将其转换为新状态后不能使用旧状态值。
Post如果我们直接设置它,怎么可能使用它的旧状态?
小智 7
如果你这样编码:
pub struct Post {
state: Box<dyn State>,
content: String,
}
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
}
impl Post {
// ...
pub fn request_review(&mut self) {
self.state = self.state.request_review();
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
你会得到一个编译器错误:
pub struct Post {
state: Box<dyn State>,
content: String,
}
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
}
impl Post {
// ...
pub fn request_review(&mut self) {
self.state = self.state.request_review();
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
这是因为调用State::request_review会 move Box<self>,它是在堆上分配的,而 Rust 不允许你只将值从堆中移开,除非你实现了Copy,否则还剩下什么?这本书用于Option::take()将所有权移出并留None在原地。
小智 5
我不认为数据在堆上这一事实,正如@fang-zhou 提到的那样,是这里的根本问题。以下代码无法编译,因为对的调用Blog::request_review尝试将state字段移出self:
impl Blog {
pub fn request_review(&mut self) {
self.state = self.state.request_review()
}
}
impl State for Draft {
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview {})
}
}
Run Code Online (Sandbox Code Playgroud)
但是,编译器不允许这样做,因为self和self.state位于引用后面。这对于堆栈上的数据来说也是一个问题:
error[E0507]: cannot move out of `self.state` which is behind a mutable reference
--> src/lib.rs:23:22
|
23 | self.state = self.state.request_review()
| ^^^^^^^^^^ ---------------- `self.state` moved due to this method call
| |
| move occurs because `self.state` has type `Box<dyn State>`, which does not implement the `Copy` trait
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,Option::take使用它是因为它允许将值从&mut引用中移出,而这通常是不允许的。源代码Option::take并依赖mem::replace于精心实施的不安全 Rust 来实现这一点。
| 归档时间: |
|
| 查看次数: |
1085 次 |
| 最近记录: |