简体   繁体   中英

How to pass a mutable self reference on to a trait method?

While working through the OOP-Chapter of the Rust book (2nd Edition) I took on the optional task to implement the method add_text for the following struct

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

There are three structs that implement the State trait, but only the Draft struct should actually do something. I implemented this as follows

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

My problem is that in order to get the State from my post struct to call the add_text method I immutably borrow self (in Post ) and can not pass on a mutable reference to the add_text method of the State trait:

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

How do I deal with this dilemma? I definitely need a mutable reference to the Post , otherwise I can't change its text. On the other hand, I need to get the State first since otherwise I can not even call the method.

One way to work around this would be be to change add_text to get_text_to_add which would not require mutability of the Post , but I would like to make sure that I am not overseeing any options to solve this.

With structs Rust is smart enough to be able to do disjoint borrows so you don't need to pass a mutable reference to the entire Post struct, just the part of it you need to modify (in this case 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);  
    }
}

This should work but it feels a bit forced, (as EvilTak points out the reference to self in Draft::add_text is redundant). I guess that's part of the point of the exercise though; while it is possible to implement certain patterns from OOP in Rust there are better ways to model the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM