# Structures

Structs are like structs/classes in other languages. Structs have fields. We can instantiate structs.

struct User {
  active: bool,
  username: String,
  email: String,
  age: u32
}

let user1 = User {
  email: String::from("user1@gmail.com"),
  username: String::from("user1"),
  active: true,
  age: 20
};

Only mutable instances may be mutated:

let mut user2 = User {
  email: String::from("user2@gmail.com"),
  username: String::from("user2"),
  active: true,
  age: 19
};

user2.active = false;

Stack

By default, Rust stores all data on the stack (like C/C++). Struct instances also are stored on the stack. Smart pointers allow changing that.

Only types whose size is known at compile time can be stored on the stack.

# Shortcuts

A bit like in JS, we can use the shorter way to instantiate objects:

fn create_object(username: String, email: String) -> User {
  User {
    username,
    email, // or email: email
    active: false,
    age: 100
  }
}

Also, we can create an instances based on other instances:

let user3 = User {
  email: String::from("user3@example.com"),
  ..user2 // must come last
};

Move

user2 was moved in the example above. Since username is a String, it was moved to user3. If the only values that were copied from user2 were active and age, there would be no move. They'd be just copied.

# Tuple Structs

Tuples may be defined with a name, like this:

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

The fields don't have names, sometimes it's ok for simple types.

# Unit-like Structs

Structs may be completely empty:

struct AlwaysEqual;

let subject = AlwaysEqual;

It's useful with traits.

# Methods

Methods are defined outside of struct definition:

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

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
  }
}

The first paramtere of a method is &self. It's a sugar syntax for self: &Self. We could also not use reference, or add mut. The first case is rare because it would take ownership. It could be useful if a method is supposed to transform an object info something else end the original object would not be needed anymore.

Impl blocks

There may be more than one impl blocks per type.

We can invoke methods like this:

let r = Rectangle();
let area = r.area(); // or Rectangle::area(&r); 

# Static methods

Methods may be defined as non-associated if they do not require an actual instance of a type. In such a case, there is no need for the first parameter to be Self. Example of such a function is String::from(). It's also useful for constuctors.

Last Updated: 1/15/2023, 6:32:34 PM