Rust OOP练习:如何将可变self引用传递给trait方法?
解决Rust中Post调用State的add_text时的可变引用问题
我刚好看过《Rust编程入门》第二版里的这个状态模式例子,你遇到的问题其实是Rust借用规则和状态模式结合时的常见坑——核心是**State trait的方法签名和Post里的调用方式没匹配上**,导致没法传递可变引用去修改Post的内容。
问题根源拆解
你最初的实现大概率是把add_text设计成只操作State自身,但我们真正要修改的是Post的content字段。如果State的add_text方法只能拿到不可变的Post引用,或者Post调用时只提供了不可变的self,自然没法突破借用规则去修改内容。而按照书中的状态模式设计,State本来就该有权限修改Post的状态或内容,所以得调整方法的参数传递逻辑。
具体解决方案
我们可以给State trait的add_text方法加一个&mut Post参数,这样只要Post自身是可变借用的,就能把自己传给State的方法。同时,只有Draft结构体需要实现实际的文本添加逻辑,其他状态(比如PendingReview、Published)直接用默认的空实现就行,符合“只有草稿能加文本”的需求。
完整代码示例
首先调整State trait,新增add_text并给默认实现:
trait State { // 书中原本就有的状态转换方法 fn request_review(self: Box<Self>) -> Box<dyn State>; fn approve(self: Box<Self>) -> Box<dyn State>; // 新增add_text,默认什么都不做 fn add_text(&self, post: &mut Post, text: &str) {} }
然后给Draft实现真正的文本添加逻辑:
struct Draft; impl State for Draft { fn request_review(self: Box<Self>) -> Box<dyn State> { Box::new(PendingReview) } fn approve(self: Box<Self>) -> Box<dyn State> { self } // 只有Draft允许修改内容 fn add_text(&self, post: &mut Post, text: &str) { post.content.push_str(text); } }
其他状态保持原有实现,不需要覆盖add_text,自动用默认空实现:
struct PendingReview; impl State for PendingReview { fn request_review(self: Box<Self>) -> Box<dyn State> { self } fn approve(self: Box<Self>) -> Box<dyn State> { Box::new(Published) } } struct Published; impl State for Published { fn request_review(self: Box<Self>) -> Box<dyn State> { self } fn approve(self: Box<Self>) -> Box<dyn State> { self } }
最后修改Post的add_text方法,用可变引用调用State的方法:
struct Post { state: Box<dyn State>, content: String, } impl Post { fn new() -> Post { Post { state: Box::new(Draft), content: String::new(), } } // 这里必须用&mut self,才能把可变的self传给state的add_text fn add_text(&mut self, text: &str) { self.state.add_text(self, text); } // 书中原本的其他方法,比如状态转换和内容获取 fn request_review(&mut self) { self.state = self.state.request_review(); } fn approve(&mut self) { self.state = self.state.approve(); } fn content(&self) -> &str { // 这里用as_any匹配具体类型,和书里的实现逻辑一致 if let Some(_) = self.state.as_any().downcast_ref::<Published>() { &self.content } else { "" } } } // 为了让State能匹配具体类型,补充as_any的实现(书里通常会有这段) use std::any::Any; trait StateExt: State { fn as_any(&self) -> &dyn Any; } impl<T: State + 'static> StateExt for T { fn as_any(&self) -> &dyn Any { self } } impl dyn State { fn as_any(&self) -> &dyn Any { self.as_any() } }
为什么这样能解决问题?
Post::add_text用&mut self,意味着我们拿到了Post的可变借用权限,符合Rust的规则,可以把它传递给State的方法。State::add_text接收&mut Post,让Draft能直接修改Post的content字段,完成文本添加。- 其他状态因为用了默认空实现,调用
add_text不会有任何效果,完美符合“只有草稿状态能加文本”的需求。
这样既遵守了Rust的借用规则,又完全实现了书中的可选任务要求。
内容的提问来源于stack exchange,提问作者m00am
相关产品推荐
相关产品推荐

