Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Struct

Defining struct

  • Definition outside of main function
  • key: value pair is called field
  • Struct’s data type is its name in the definition
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

Instantiating struct

fn main() {
    let mut user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("[email protected]"),
        sign_in_count: 1,
    };

    // if the instance is mutable its values can be changed
    user1.email = String::from("[email protected]");
}

Function instantiating struct

fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: username,
        email: email,
        sign_in_count: 1,
    }
}

Field Init Shorthand

  • nor need to repeat key: value if the function’s parameters and field’s names are the same
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username,
        email,
        sign_in_count: 1,
    }
}

Struct Update

  • new instance of struct includes values from another instance of the same type and updates the others
fn main() {
    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("[email protected]"),
        sign_in_count: user1.sign_in_count,
    };
}
  • it can be further simplified to:
fn main() {
    let user2 = User {
        email: String::from("[email protected]"),
        ..user1
    };
}
  • Struct update either moves/copies data depending on data types
  • In the example above user1 cannot be used anymore as its username changed the ownership due to its String data type

Tuple structs

  • Similar to tuples but the tuple itself has a name
  • Each tuple struct is its own type
  • As normal tuples:
    • no names associated with their fields
    • they can be destructured
    • individual values can be accessed with . followed by the index
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}

Unit-like struct

  • similar to unit type () (empty tuple)
struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

Method syntax

  • Similar to functions:
    • Declared with fn
    • Can take parameters and have return value
    • Contain code
  • Defined in the context of struct
  • Their first parameter is always self which represents the instance of the struct the method is being called on
  • Methods can take ownership so you can define parameter:
    • self (takes ownership)
    • &self (reference borrowing)
    • &mut self (mutable reference)
  • Note: parameter &self is equivalent of self: &self
  • Defined within impl (implementation) block
#[derive(Debug)] // allows to print struct with println macro
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

Multiple parameters in method

  • other parameters can be defined after self parameter as in function
fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

Associated functions

  • All functions defined within impl block are associated functions
  • They are associated with the type named after impl
  • Associated function is not method if parameter self is not present (they do not need an instance to work with)
  • Often used for constructors returning a new instance of struct
  • self in the return type is alias for the type that follows impl
  • Associated function is called with ::
fn main() {
   let sq = Rectangle::square(3);
}

impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}