One of the things that’s “hard” about Rust when you first start learning it are the different ways you can pass an object to a function or return one, and the types associated with them.
For example, Strings come in a few different flavors, and you need to know which one to return them:
// This works:
fn return_string() -> String {
let not_global = "I return ok, because I'm not global.".to_string();
not_global // Funky Rust return syntax.
}
// This doesn't, uncomment to see the error
/*
fn return_broken() -> String {
let broken = "Not OK -- I'm a global literal!";
broken
}
*/
println!("{}", return_string());
Mutable methods vs immutable methods
Before learning about passing objects, we need to create a basic “object” and test it, which has mutable and immutable methods.
pub struct Person {
name: String,
state: String,
}
impl Person {
// See https://rust-unofficial.github.io/patterns/idioms/ctor.html
pub fn new(name: String, state: String) -> Self {
Self {
name: name,
state: state
}
}
pub fn print(&self) {
println!("{} lives in {}.", self.name, self.state);
}
// Note it's &mut, not mut&
pub fn move_to(&mut self, state: String) {
self.state = state;
}
}
fn main() {
let static_john = Person::new("John Lockwood".to_string(), "California".to_string());
static_john.print();
// Static John can't move! Next line is an error
// static_john.move_to("North Carolina".to_string());
// Mutable John can
let mut john = Person::new("John Lockwood".to_string(), "California".to_string());
john.print();
john.move_to("North Carolina".to_string());
john.print();
}
Passing Objects
Basically, there are about four ways things can get passed around:
- Directly non-mutable
- Directly mutable
- By reference non-mutable
- By reference mutable
Passing Objects Directly
This MOVES the object to a new owner. If you do this, you can’t use the reference anymore. It doesn’t matter if you pass it mutably or not. If you’re not passing a reference, you’re changing ownership.
fn pass_ownership_non_mutable(person: Person) {
person.print();
}
fn pass_ownership_mutably(mut person: Person) {
person.move_to("New York".to_string());
}
Calling:
// Pass a person not by reference
let john2 = Person::new("John".to_string(), "California".to_string());
pass_ownership_non_mutable(john2);
// Can't do -- borrowed after move: E0382:
// john2.print();
// Note we don't need to declare this as mut here
// for that it matches the function signature of pass_ownership_mutually
let john3 = Person::new("John ".to_string(), "California".to_string());
pass_ownership_mutably(john3);
// Can't do -- borrowed after move: E0382:
// john3.print();
Passing By Reference
In contrast to passing a non-reference, passing a reference does not change the owner:
fn pass_reference_non_mutable(person: &Person) {
person.print();
}
fn pass_reference_mutably(person: &mut Person) {
person.move_to("Washington".to_string());
person.print();
}
Calling:
let mut john3 = Person::new("John ".to_string(), "California".to_string());
pass_reference_mutably(&mut john3);
// Reusing, ownership not transfered!
pass_reference_non_mutable(&john3);