Image

Rust Struct

Structs are similar to Classes in other languages. A “blueprint” for objects that can be instantiated.

// creating a new struct
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

// instantiating that struct by populating the fields with data
// note order of fields does not matter
let user1 = User {
	active: true, 
	username: String::from("someusername123"), 
	email: String::from("someone@example.com"), 
	sign_in_count: 1, 
};

Here String is used instead of &str so that each instance of the struct owns all of its data and that data is valid for as long as the entire struct is valid. (In order for structs to store references to data owned by something else, lifetimes are required.)

And here is a function to build particular struct instances. Must be in the same scope as the struct.

fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: username, // can be shorthanded to `username,`
        email: email,       // can be shorthanded to `email,`
        sign_in_count: 1,
    }
}

Recycling old values from another struct to a new one using update syntax.

fn main() {
    // --snip--
    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };
}

// even shorter, but the ..user1 must come at the end
fn main() {
    // --snip--
    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };
}

Structs without any fields are called unit-like structs and they can be useful to implement traits on some type without needing any data that you want to store on the type itself.

Accessing fields of a borrowed struct instance does not move the field values.

struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}

In order to print structs, you must use {:?} in the println! string, but also you need to add a outer attribute (decorator) to the struct to opt into debugging mode #[derive(Debug)] just before the struct definition.

Adding member functions or methods requires us to set up an impl block that refers to the struct, and within here define the functions, but each function within must start with a reference to self.

#[derive(Debug)] 
struct Rectangle { 
	width: u32, 
	height: u32, 
} 
impl Rectangle { 
	fn area(&self) -> u32 { 
		self.width * self.height 
	} 
}

One can make make functions that are associated with the struct but are not methods. Below is an example that’s similar to a constructor.

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

// to call it:
let sq = Rectangle::square(3);

This function is namespaced by the struct: the :: syntax is used for both associated functions and namespaces created by modules.

© Filip Niklas 2024. All poetry rights reserved. Permission is hereby granted to freely copy and use notes about programming and any code.