Structs

Structs

Structuring data into structs

A struct, or structure, is a custom data type that lets you package together and name multiple related values that make up a meaningful group.

You have already seen them when studying C, but Rust's structs are much more powerful.

Structs

Structs in C


#include <stdint.h>
#include <stdio.h>
struct Color {
  uint8_t r;
  uint8_t g;
  uint8_t b;
};

typedef struct Point {
  int x;
  int y;
} Point_t;
      

int main(void) {
  struct Color color = {
      .r = 230,
      .g = 100,
      .b = 10,
  };
  printf("%d %d %d\n", color.r, color.g, color.b);

  struct Color black = {0};
  printf("%d %d %d\n", black.r, black.g, black.b);

  Point_t point;
  point.x = 10; point.y = -10;
  printf("%d %d\n", point.x, point.y);
  return 0;
}
      
Structs

Structs in Rust

Structs

Mutating a struct

Structs

Field init shorthand

If there is already a variable with the same name of the field, you can avoid repeating yourself.

Structs

Struct update syntax

Do you find yourself wanting to copy some values from one struct to another?

The update syntax is for this situation. Set the fields you want to be different, in any order and then write ..[copied_struct_name].

Structs

Struct update syntax

Structs

Debugging structs

#[derive(Debug)] will automatically implement a function that println!("{:?}") will call to print the values of your struct.

The function called by {:?} can be generated automatically, but you can't do the same for {}.

Btw you can also use {var:?} to use the debug function on var, or {:#?} to have nicer looking debug output.
Structs

Most common use for update

It's common for structs, especially if they have many fields, to have a default that you can copy from, instead of writing a value for all fields.

Structs

Default

Default::default() is a function that returns a default value for a type.

We can decide to implement the function ourselves (how to do it will be explained on day 4), or tell Rust to call Default::default() automagically on all fields recursively with #[derive(Default)].

Structs

Default

For std library types like numbers, String, Vec, etc. the function is implemented by the std library. But if the type doesn't have a Default::default(), derive will fail.

Structs

Tuple structs

The same as normal tuples, but it has a specific name instead of having to specify the types each time.

Structs

Ownership of struct data

Structs

Ownership of struct data

Vec is an owned type, which means that the struct owns completely all the data in the scores fields.

Meanwhile the slice type is borrowed, which means that the compiler needs some kind of assurance that the scores, and whoever owns them, will exist at least as long as the struct.

Remember lifetimes?
Structs

Ownership of struct data

Structs own all the memory of their fields, and therefore borrowing a field from a struct is (in terms of borrow checking not of actual assembly) the same as borrowing all the struct.

Structs

Functions with structs in C


#include <stdio.h>

struct Rectangle {
  float width;
  float height;
};

struct Rectangle rectangle_new(float width, float height) {
  struct Rectangle rect = {.width = width, .height = height};
  return rect;
}

float rectangle_area(struct Rectangle *self) {
  return self->height * self->width;
}

int main(void) {
  struct Rectangle rect = rectangle_new(10.0, 30.0);
  printf("%f\n", rectangle_area(&rect));
  return 0;
}
    
Structs

Functions with structs in Rust

Structs

The impl block

It's better to define functions related to a struct inside an impl block. This way they will be easier to find and use.

Inside the impl block we can also use Self, which is a shorthand that is replaced with the name of the type we are implenting the functions for, and self which is an argument implicitly of type Self.

Structs

Struct methods

Structs

Method syntax

The :: is like a path to tell the compiler where to find a method, for example Rectangle::new() tells it to look for the new() function inside Rectangle's impl block.

This is called namespacing.

C doesn't have namespacing, which means that name collisions are extremely common for large projects.

Structs

Method syntax

rect.area() is the same as Rectangle::area(&rect), the '.' simply passes the variable as the self argument of the function.

It can also pass the reference or dereference to the variable without adding '&' or '*', simply by looking at the function signature.