在这段代码中:
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
}
}
存在对take()
的调用;书上说:
要使用旧的状态,request_review方法需要取得状态值的所有权,这就是Post的state字段中的Option的作用所在:我们调用take方法将Some值从state字段中取出,并在其位置上保留None。
我们需要暂时将state设置为None,而不是直接用self.state = self.state.request_review();
这样的代码来获得state值的所有权,这样可以确保Post在我们将其转换为新state后不能使用旧的state值。
如果我们直接设置Post
,它怎么可能使用它的旧状态呢?
4条答案
按热度按时间0s7z1bwu1#
如果你这样写代码:
您将得到一个编译器错误:
这是因为调用
State::request_review
会移动Box<self>
,它是在heap上分配的,Rust不允许你从heap中移走值,除非你实现了Copy
,否则那里还剩下什么?本书使用Option::take()
来移出所有权,并将None
留在原处。ymzxtsji2#
如果
request_review
死机,将导致释放Box
两次,第一次是在request_review
中,第二次是在释放Option
时。nbnkbykc3#
引自书中;粗体我的格式
我们调用take方法从state字段中取出Some值并在其位置保留None,因为Rust不允许我们在结构体中有未填充的字段。
至于同一语句中的移动和赋值,看起来Rust没有检测到它是有效的。
hgncfbus4#
我不认为数据在堆上的事实,正如@xiang-zhou提到的,是这里的基本问题,下面的代码不能编译,因为对
Blog::request_review
的调用试图将state
字段移出self
:但是,编译器不允许这样做,因为
self
和self.state
在引用之后,这对于堆栈上的数据也是一个问题:为了解决这个问题,我们使用
Option::take
,因为它允许从&mut
引用中移出一个值,这通常是不允许的。Option::take
和mem::replace
的源代码依赖于精心实现的unsafe rust来实现这一点。