You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
    }
}

为什么这样能解决问题?

  1. Post::add_text用&mut self,意味着我们拿到了Post的可变借用权限,符合Rust的规则,可以把它传递给State的方法。
  2. State::add_text接收&mut Post,让Draft能直接修改Post的content字段,完成文本添加。
  3. 其他状态因为用了默认空实现,调用add_text不会有任何效果,完美符合“只有草稿状态能加文本”的需求。

这样既遵守了Rust的借用规则,又完全实现了书中的可选任务要求。

内容的提问来源于stack exchange,提问作者m00am

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:59:30