Reputation: 6298
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.
Upvotes: 7
Views: 4240
Reputation: 403
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.
Upvotes: 4