Rust struct

  • Seen a bunch of these already

  • Basic idea is the same as structures in any language

  • Rust structs are fancy, and an essential building block

  • Most of the standard API is built out of struct, enum, trait

Basics

  • Basic decl syntax is straightforward

      struct Point {
          x: i64,
          y: i64,
      }
    
  • There is no default constructor: can create with again-obvious syntax

      let p = Point { x: 5, y: -2 };
    

Fancy Features

  • Common case of tag-name = varname is special

      let x = 5;
      let p = Point { x, y: -2 };
    
  • Can have empty structures: these are inhabited by only one value

      struct Empty;
    
      let e = Empty;
    
  • "Tuple structs" have anonymous fields

      struct Point(i64, i64);
    
      let p = Point(3, -2);
      let x = p.0;
    
  • Single-element tuple structs are often used for "newtyping"

      struct Temperature(i64);
    

Ownership and Lifetimes

  • Ownership is a bit complicated with structs

  • A struct value owns all of its fields individually

      struct TwoString {
          s1: String,
          s2: String,
      }
    
  • Moving a field out of a struct makes it "partially moved"

      let t = TwoString {
          s1: "hello".to_string(),
          s2: "world".to_string(),
      };
      let s1 = t.s1;
      println!("{}", s1); // prints "hello"
      // println!("{}", t.s1); // compiler error
      // return t; // compiler error: t partially moved
    
  • References stored in structs need to outlast the struct

      struct TwoStr<'a, 'b> {
          s1: &'a str,
          s2: &'b str,
      }
    
      let s = "hello".to_string();
      let t = TwoStr { s1: &s, s2: "world" };
      drop(s);
      println!("{}", t.s1); // prints "hello"
    
  • Normally need to explicitly specify reference lifetime

impl

  • Mechanism for getting that fancy OO-style syntax

  • Implementor defines whether self argument is by move, reference or mut reference (examples/point.rs)

  • Allows chaining of operators, which can be syntactically nice

Deriving

  • Mechanism for getting traits defined automatically for your struct

  • Uses attribute syntax

  • Common kind of thing to write (examples/units.rs)

      #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
      struct Temperature(i64);
    

Pattern Matching

  • Not specific to structs / enums

  • Mechanism for deconstructing a composite thing

      let (x, h) = (3, "hello");
      println!("{} {}", x, h);
      let &[v1, v2, v3] = &[4, 5, 6];
      println!("{}", v1 + v2 + v3);
    
  • We've seen some of this before.

  • Two kinds: "refutable" and "irrefutable"

    • let requires irrefutable pattern
    • match allows refutable pattern, but all possible patterns must be matched

Match

  • The match statement is the canonical pattern matching tool

      struct Point {
          x: i64,
          y: i64,
      }
    
      let p = Point{x: 3, y: 5};
      let z = match p {
          Point{x: 3, y: 5} => {
              println!("is good");
              8
          }
          _ => {
              panic!("bad point");
          }
      };
      match p {
          Point{x: xx@1...3, ..} => println!("{}", xx),
          Point{x: xx@0, y: 4} => println!("{}", xx),
          _ => panic!("bad point"),
      }
    
  • Note the existence of .., ..., | (not shown, only at top level), @

  • First match is chosen

  • Compiler checks that every possible value will be matched, and that later matches are not subsumed by earlier

Pattern Variable Modifiers

  • Rules for move/borrow are the same for pattern variables

  • For convenience, you can modify a pattern variable with ref or ref mut to have it borrowed instead of moved

Misc

  • Struct layout concepts are in the book: not too important

  • Need to master structs: they are used everywhere

Last modified: Monday, 26 April 2021, 11:33 PM