Passing Objects

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 use to return them.:

#![allow(unused)]

fn main() {
// 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 any more. I.e. Can do it mutably or not.

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

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