Structs

Like C and C++, Rust has support for custom structs:

struct Person {
    name: String,
    age: u8,
}
 
fn main() {
    let mut peter = Person {
        name: String::from("Peter"),
        age: 27,
    };
    println!("{} is {} years old", peter.name, peter.age);
 
    peter.age = 28;
    println!("{} is {} years old", peter.name, peter.age);
 
    let jackie = Person {
        name: String::from("Jackie"),
        ..peter
    };
    println!("{} is {} years old", jackie.name, jackie.age);
}
Peter is 27 years old
Peter is 28 years old
Jackie is 28 years old

Notes

  • Structs work like in C or C++.
    • Like in C++, and unlike in C, no typedef is needed to define a type.
    • Unlike in C++, there is no inheritance between structs.
  • Methods are defined in an impl block, which we will see in following slides.
  • This may be a good time to let people know there are different types of structs.
    • Zero-sized structs e.g., struct Foo; might be used when implementing a trait on some type but don’t have any data that you want to store in the value itself.
    • The next slide will introduce Tuple structs, used when the field names are not important.
  • The syntax ..peter allows us to copy the majority of the fields from the old struct without having to explicitly type it all out. It must always be the last element.

Tuple Structs

If the field names are unimportant, you can use a tuple struct:

struct Point(i32, i32);
 
fn main() {
    let p = Point(17, 23);
    println!("({}, {})", p.0, p.1);
}
(17, 23)

This is often used for single-field wrappers (called newtypes):

struct PoundsOfForce(f64);
struct Newtons(f64);
 
fn compute_thruster_force() -> PoundsOfForce {
    todo!("Ask a rocket scientist at NASA")
}
 
fn set_thruster_force(force: Newtons) {
    // ...
}
 
fn main() {
    let force = compute_thruster_force();
    set_thruster_force(force);
}

Notes

  • Newtypes are a great way to encode additional information about the value in a primitive type, for example:
    • The number is measured in some units: Newtons in the example above.
    • The value passed some validation when it was created, so you no longer have to validate it again at every use: ’PhoneNumber(String)orOddNumber(u32)`.
  • Demonstrate how to add a f64 value to a Newtons type by accessing the single field in the newtype.
    • Rust generally doesn’t like inexplicit things, like automatic unwrapping or for instance using booleans as integers.
    • Operator overloading is discussed on Day 3 (generics).
  • The example is a subtle reference to the Mars Climate Orbiter failure.

Field Shorthand Syntax

If you already have variables with the right names, then you can create the struct using a shorthand:

#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}
 
impl Person {
    fn new(name: String, age: u8) -> Person {
        Person { name, age }
    }
}
 
fn main() {
    let peter = Person::new(String::from("Peter"), 27);
    println!("{peter:?}");
}
Person { name: "Peter", age: 27 }

Notes

  • The new function could be written using Self as a type, as it is interchangeable with the struct type name
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }
    impl Person {
        fn new(name: String, age: u8) -> Self {
            Self { name, age }
        }
    }
  • Implement the Default trait for the struct. Define some fields and use the default values for the other fields.
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }
    impl Default for Person {
        fn default() -> Person {
            Person {
                name: "Bot".to_string(),
                age: 0,
            }
        }
    }
    fn create_default() {
        let tmp = Person {
            ..Person::default()
        };
        let tmp = Person {
            name: "Sam".to_string(),
            ..Person::default()
        };
    }
  • Methods are defined in the impl block.
  • Use struct update syntax to define a new structure using peter. Note that the variable peter will no longer be accessible afterwards.
  • Use {:#?} when printing structs to request the Debug representation.

Reference List

  1. https://google.github.io/comprehensive-rust/structs.html