如何将可变的self引用传递给trait方法?

7

在阅读Rust书籍(第二版)的OOP-Chapter时,我尝试实现以下结构体的可选方法add_text

pub struct Post {
    state: Option<Box<State>>,
    content: String,
}

有三个结构体实现了State特质,但只有Draft结构体应该实际执行某些操作。我按照以下方式实现了这一点。
trait State {
    // snip
    fn add_text(&self, post: &mut Post, text: &str) { }
}


struct Draft { }

impl State for Draft {
    // snip
    fn add_text(&self, post: &mut Post, text: &str) {
        post.content.push_str(text);
    }
}

我的问题是,为了从我的post结构中获取State以调用add_text方法,我需要对self进行不可变借用(在Post中),无法传递可变引用到State trait的add_text方法中。
impl Post {
    // snip

    pub fn add_text(&mut self, text: &str){
        let state = self.state.as_ref().unwrap();  // This immutably borrows self
        state.add_text(self, text);  // so that this mutable borrow is no longer possible
    }
}

我该如何处理这个困境?我肯定需要一个可变的Post引用,否则我无法改变它的文本。另一方面,我需要先获取State,否则我甚至不能调用方法。
解决这个问题的一种方法是将add_text更改为get_text_to_add,这不需要对Post进行可变性,但我想确保我没有忽略任何解决此问题的选项。

1
这对我来说看起来像是代码异味。首先,Draft 上的 State::add_text 方法实现与 Draft 结构体完全没有关系 - 你可以删除 &self 参数并完成它。其次,作为这个假想库的外部使用者,我期望 add_text 方法在 Post 结构体本身上。将 add_text 移动到 Post 中,并根据需要查找信息或改变 state 字段会更好。如果您只想在帖子是草稿时添加文本,则可以在 Post::add_text 中检查 self.state 是否为 Draft - EvilTak
1个回答

4

通过使用结构体,Rust 足够智能,可以进行不相交的借用,因此您不需要传递对整个 Post 结构体的可变引用,只需要修改部分内容即可(在本例中为 content)。

trait State {
    // snip

    // Modify the method on the add_text trait so that it
    // takes a mutable reference to String
    fn add_text(&self, content: &mut String, text: &str) { }
}

struct Draft { }

impl State for Draft {
    // snip

    // Update the implementation of State for Draft so that it
    // matches the new signature
    fn add_text(&self, content: &mut String, text: &str) {
        content.push_str(text);
    }
}

impl Post {
    // snip

    pub fn add_text(&mut self, text: &str){
        let state = self.state.as_ref().unwrap();  

        // Now when you call add_text you don't require a mutable 
        // reference to self, just to self.content and so the 
        // borrow checker is happy
        state.add_text(&mut self.content, text);  
    }
}

这应该是可行的,但感觉有点勉强(正如EvilTak指出的那样,在Draft::add_text中对self的引用是多余的)。不过我想这也是练习的一部分;虽然在Rust中可以实现OOP的某些模式,但有更好的方法来建模问题。


谢谢,我没有想到这种方法。我同意你和@EvilTak的看法,这很可能是一个不好的做法(因此,我在问题的最后一段提到了这一点)。另一方面,这是一个学习借用更多知识的好机会,所以感谢你的帮助。我将在周一接受答案。 - m00am

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接