@Kais(VagrantPi)

tags: Rust, 簡報, Rust 讀書會

Defining and Instantiating Structs


Defining Structs

struct User {
  username: String,
  email: String,
  sign_in_count: u64,
  active: bool,
}

Structs 跟 tuples 很像,一樣可以包含多種 type,跟 tuples 不同的是每個 value 都有一個 key,所以可以清楚知道該值是啥


Instantiating Structs

  • key: value pairs

  • 不須按造順序

let user1 = User {
  email: String::from("someone@example.com"),
  username: String::from("someusername123"),
  active: true,
  sign_in_count: 1,
};

裡面有寫到一個不錯的比喻

the struct definition is like a general template for the type, and instances fill in that template with particular data to create values of the type


更改值


let mut user1 = User {
  email: String::from("someone@example.com"),
  username: String::from("someusername123"),
  active: true,
  sign_in_count: 1,
};

user1.email = String::from("anotheremail@example.com");

return Instance

fn build_user(email: String, username: String) -> User {
  User {
    email: email,
    username: username,
    // 因為輸入參數名稱跟 struct 變數名稱一樣,所以可以改寫成
    // email,
    // username,
    active: true,
    sign_in_count: 1,
  }
}

Creating Instances From Other Instances With Struct Update Syntax
新的 Instance 有部分資料跟使用其他的 Instance 時,
可以透過 struct update syntax(..) 的方法將其餘沒寫到的變數資料補上
let user2 = User {
  email: String::from("another@example.com"),
  username: String::from("anotherusername567"),
  active: user1.active,
  sign_in_count: user1.sign_in_count,
};

// --------------------------------------------------

let user2 = User {
  email: String::from("another@example.com"),
  username: String::from("anotherusername567"),
  ..user1
};

tuple structs

struct 跟 tuple 的混合型別,取值時是使用 index 值
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let mut black = Color(0, 0, 0);
let origin = Point(1, 2, 3);

println!("{}", origin.1);
// 2

black.0 = 1
println!("{}", black.0);
// 1

An Example Program Using Structs


v0.1

fn main() {
  let width1 = 30;
  let height1 = 50;

  println!(
    "The area of the rectangle is {} square pixels.",
    area(width1, height1)
  );
  // The area of the rectangle is 1500 square pixels.
}

fn area(width: u32, height: u32) -> u32 {
  width * height
}

v.02(使用 tuple 重構)

fn main() {
  let rect1 = (30, 50);

  println!(
    "The area of the rectangle is {} square pixels.",
    area(rect1)
  );
}

fn area(dimensions: (u32, u32)) -> u32 {
  dimensions.0 * dimensions.1
}

v.03(使用 struct 重構)

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
}

Adding Useful Functionality with Derived Traits

如果是著想印出 struct

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

fn main() {
  let rect1 = Rectangle { width: 30, height: 50 };

  println!("rect1 is {}", rect1);
  // error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied
  // `Rectangle` cannot be formatted with the default formatter; try using
  // `:?` instead if you are using a format string
}

println! 在印出東西時 {} 可以包含著格式化特徵(Formatting traits) 而 Display 為預設值

nothing ⇒ Display
? ⇒ Debug
x? ⇒ Debug with lower-case hexadecimal integers
X? ⇒ Debug with upper-case hexadecimal integers
o ⇒ Octal
x ⇒ LowerHex
X ⇒ UpperHex
p ⇒ Pointer
b ⇒ Binary
e ⇒ LowerExp
E ⇒ UpperExp

之所以 Display 能印出原始 type,是因為都們很"單純",所以可以清楚的知道要印出什麼,但 struct 不同,包東包西,struct 的 key value 間還隔著 :,所以到底該印那些你並沒有說,而 error 也有建議可以使用 :? 來試試


當你試了之後會發現

// error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
// `Rectangle` cannot be formatted using `:?`; if it is defined in your
// crate, add `#[derive(Debug)]` or manually implement it


讓我們再印一次

#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}

fn main() {
  let rect1 = Rectangle { width: 30, height: 50 };

  println!("rect1 is {:?}", rect1);
  // rect1 is Rectangle { width: 30, height: 50 }
}

如果 struct 很大很複雜

這樣我們會改用 {:#?}

// rect1 is Rectangle {
//   width: 30,
//   height: 50
// }

Method Syntax


Defining Methods

#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}

impl Rectangle {
  fn area(&self) -> u32 {
    self.width * self.height
  }
}

fn main() {
  let rect1 = Rectangle { width: 30, height: 50 };

  println!(
    "The area of the rectangle is {} square pixels.",
    rect1.area()
  );
}

Methods with More Parameters

fn main() {
  let rect1 = Rectangle { width: 30, height: 50 };
  let rect2 = Rectangle { width: 10, height: 40 };
  let rect3 = Rectangle { width: 60, height: 45 };

  println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
  println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
  // want
  // Can rect1 hold rect2? true
  // Can rect1 hold rect3? false
}

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

Associated Functions

Associated Functions 通常用於建立一個或某種資料型態(因為會直接 return struct 的 instance)

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

let sq = Rectangle::square(3);

Multiple impl Blocks

impl Rectangle {
  fn area(&self) -> u32 {
    self.width * self.height
  }
}

impl Rectangle {
  fn can_hold(&self, other: &Rectangle) -> bool {
    self.width > other.width && self.height > other.height
  }
}

Thanks for listening ~

Select a repo