Mutable references
Your accessor methods should look like this now:
#![allow(unused)] fn main() { impl Ticket { pub fn title(&self) -> &String { &self.title } pub fn description(&self) -> &String { &self.description } pub fn status(&self) -> &String { &self.status } } }
A sprinkle of &
here and there did the trick!
We now have a way to access the fields of a Ticket
instance without consuming it in the process.
Let's see how we can enhance our Ticket
struct with setter methods next.
Setters
Setter methods allow users to change the values of Ticket
's private fields while making sure that its invariants
are respected (i.e. you can't set a Ticket
's title to an empty string).
There are two common ways to implement setters in Rust:
- Taking
self
as input. - Taking
&mut self
as input.
Taking self
as input
The first approach looks like this:
#![allow(unused)] fn main() { impl Ticket { pub fn set_title(mut self, new_title: String) -> Self { // Validate the new title [...] self.title = new_title; self } } }
It takes ownership of self
, changes the title, and returns the modified Ticket
instance.
This is how you'd use it:
#![allow(unused)] fn main() { let ticket = Ticket::new( "Title".into(), "Description".into(), "To-Do".into() ); let ticket = ticket.set_title("New title".into()); }
Since set_title
takes ownership of self
(i.e. it consumes it), we need to reassign the result to a variable.
In the example above we take advantage of variable shadowing to reuse the same variable name: when
you declare a new variable with the same name as an existing one, the new variable shadows the old one. This
is a common pattern in Rust code.
self
-setters work quite nicely when you need to change multiple fields at once: you can chain multiple calls together!
#![allow(unused)] fn main() { let ticket = ticket .set_title("New title".into()) .set_description("New description".into()) .set_status("In Progress".into()); }
Taking &mut self
as input
The second approach to setters, using &mut self
, looks like this instead:
#![allow(unused)] fn main() { impl Ticket { pub fn set_title(&mut self, new_title: String) { // Validate the new title [...] self.title = new_title; } } }
This time the method takes a mutable reference to self
as input, changes the title, and that's it.
Nothing is returned.
You'd use it like this:
#![allow(unused)] fn main() { let mut ticket = Ticket::new( "Title".into(), "Description".into(), "To-Do".into() ); ticket.set_title("New title".into()); // Use the modified ticket }
Ownership stays with the caller, so the original ticket
variable is still valid. We don't need to reassign the result.
We need to mark ticket
as mutable though, because we're taking a mutable reference to it.
&mut
-setters have a downside: you can't chain multiple calls together.
Since they don't return the modified Ticket
instance, you can't call another setter on the result of the first one.
You have to call each setter separately:
#![allow(unused)] fn main() { ticket.set_title("New title".into()); ticket.set_description("New description".into()); ticket.set_status("In Progress".into()); }
Exercise
The exercise for this section is located in 03_ticket_v1/07_setters