Files
100-exercises-to-learn-rust/book/src/03_ticket_v1/01_struct.md
Luca Palmieri 96f06708b0 Render the book in PDF using pandoc and LaTeX. (#126)
* Render the book in PDF using `pandoc` and LaTeX.

* Fix installs.

* Go the apt-get route

* Another attempt

* Avoid installing twice.

* Re-order.

* Add more packages.

* Minimise deps. Fix link checker.

* Missing package.

* Missing package.

* Missing package.

* More packages.

* Missing package.

* Missing package.

* More packages...

* Remove.

* Fix link checker.

* Fix link checker.

* Fix path.

* Add subtitle.

* Avoid running over the right margin.

* Avoid running over the right margin.

* Formatting
2024-08-05 17:52:15 +02:00

3.5 KiB
Raw Blame History

Structs

We need to keep track of three pieces of information for each ticket:

  • A title
  • A description
  • A status

We can start by using a String to represent them. String is the type defined in Rusts standard library to represent UTF-8 encoded text.

But how do we combine these three pieces of information into a single entity?

Defining a struct

A struct defines a new Rust type.

struct Ticket {
    title: String,
    description: String,
    status: String
}

A struct is quite similar to what you would call a class or an object in other programming languages.

Defining fields

The new type is built by combining other types as fields.
Each field must have a name and a type, separated by a colon, :. If there are multiple fields, they are separated by a comma, ,.

Fields dont have to be of the same type, as you can see in the Configuration struct below:

struct Configuration {
   version: u32,
   active: bool
}

Instantiation

You can create an instance of a struct by specifying the values for each field:

// Syntax: <StructName> { <field_name>: <value>, ... }
let ticket = Ticket {
    title: "Build a ticket system".into(),
    description: "A Kanban board".into(),
    status: "Open".into()
};

Accessing fields

You can access the fields of a struct using the . operator:

// Field access
let x = ticket.description;

Methods

We can attach behaviour to our structs by defining methods.
Using the Ticket struct as an example:

impl Ticket {
    fn is_open(self) -> bool {
        self.status == "Open"
    }
}

// Syntax:
// impl <StructName> {
//    fn <method_name>(<parameters>) -> <return_type> {
//        // Method body
//    }
// }

Methods are pretty similar to functions, with two key differences:

  1. methods must be defined inside an impl block
  2. methods may use self as their first parameter. self is a keyword and represents the instance of the struct the method is being called on.

self

If a method takes self as its first parameter, it can be called using the method call syntax:

// Method call syntax: <instance>.<method_name>(<parameters>)
let is_open = ticket.is_open();

This is the same calling syntax you used to perform saturating arithmetic operations on u32 values in the previous chapter.

Static methods

If a method doesnt take self as its first parameter, its a static method.

struct Configuration {
    version: u32,
    active: bool
}

impl Configuration {
    // `default` is a static method on `Configuration`
    fn default() -> Configuration {
        Configuration { version: 0, active: false }
    }
}

The only way to call a static method is by using the function call syntax:

// Function call syntax: <StructName>::<method_name>(<parameters>)
let default_config = Configuration::default();

Equivalence

You can use the function call syntax even for methods that take self as their first parameter:

// Function call syntax:
//   <StructName>::<method_name>(<instance>, <parameters>)
let is_open = Ticket::is_open(ticket);

The function call syntax makes it quite clear that ticket is being used as self, the first parameter of the method, but its definitely more verbose. Prefer the method call syntax when possible.