Skip to content

结构体

命名结构体 named struct

named struct 和 Golang 的 struct 类似。

定义结构体:

1
2
3
4
5
struct 结构体名 {
    字段名: 类型,
    字段名: 类型,
    ......
}

构造结构体实例:

let 实例名 = 结构体名{
    字段名: ,
    字段名: ,
    ......
}

let 实例名 = 结构体名{
    ,
    ,
    ......
}

let 实例名2 = 结构体名{
    字段名: ,
    字段名: ,
    ..实例名1
}

实例化结构体时,可以写出字段,这样赋值可以不按照定义时的字段的顺序,也可以不写出字段,但这样必须按照定义时的字段顺序。

除此之外还可以使用 ..其他实例名 的方式借用或拷贝其他实例剩余字段的值。

Warning

注意 Rust 的结构体在构造时需给所有字段赋值。

使用第三种方式的时候,如果被拷贝的实例的字段不是可拷贝的,那么它的所有权会被移到新的结构体上。

struct Person{
    name: String,
    age u32,
}

fn main() {
    let s1 = Person{
        name: String::from("Boii");
        age: 18,
    };

    let s2 = Person{
        ..s1
    }
    println!("{}", s2.age);     // 18
    println!("{}", s2.name);    // Boii

    println!("{}", s1.age);     // age 是可拷贝的,所以 s1.age 不会失去所有权
    println!("{}", s1.name);    // s1 的 name 字段已经被移到 s2.name 上了,所以这里 s1.name 已经变成为初始化的状态,打印她会报错
}

如果不希望 s1 失去所有权,可以使用 .clone()。

fn main() {
    let s1 = Person{
        name: String::from("Boii");
        age: 18,
    };

    let s2 = Person{
        ..s1.clone()    // 使用克隆
    }
    println!("{}", s2.age);     // 18
    println!("{}", s2.name);    // Boii

    println!("{}", s1.age);     // age 是可拷贝的,所以 s1.age 不会失去所有权
    println!("{}", s1.name);    // s1 的 name 字段只是被克隆,所以这里 s1.name 依然不变,可以正常使用
}

元组结构体 tuple struct

除了 named struct 之外, Rust 提供了一种 tuple struct (元组结构体),非常适合一些少量字段的结构。

例如颜色、坐标:

struct Color(u8, u8, u8);
struct Point2d(f64, f64);

let red = Color(0xFF, 0, 0);// 赋值
let Color(r, g, b) = red;   // 解构

空结构体 unit-like struct

结构体内部可以没有任何字段,这样的结构体实例内存占用为 0,但定义后面不可以加大括号(除了1.9 nightly)。

但我们可以对空结构体实现她的成员函数。

1
2
3
4
5
6
7
struct emptySt;

let empty = emptySt;

impl someTrait for emptySt {
    ...
}

当我们只想关心该类型的行为,不想关心该类型的内容时,就非常适合使用空结构体。

struct 的方法

定义 struct 方法使用 impl STRUCT_NAME{},形式如下:

1
2
3
4
impl STRUCT_NAME {
    <METHOD SET>
    ...
}

Example

struct Person {
    name: String,
    age: u32,
}

impl Person { // 定义结构体 Person 的方法
    fn get_name(&self) -> &str {
        &self.name
    }

    fn get_age(&self) -> u32 {
        self.age
    }
}

fn main() {
    let person = Person {
        name: "Boii".to_string(),
        age: 18,
    };
    println!("name: {:?}", person.get_name());
    println!("age:  {:?}", person.get_age());
}

可以将多个方法定义在多个 impl STRUCT_NAME{} 块中

self、&self、&mut self

  • self 表示结构体实例的所有权转移到该方法中,这种形式较少,因为这会使得调用方法后对象立即消失。但有时候也能派上场,例如可用于替换对象:调用方法后原对象消失,但返回另一个替换后的对象。;
  • &self 表示该方法对结构体实例的不可变借用;适用于 getter;
  • &mut self 表示方法对结构体的可变借用;适用于 setter;
struct Person {
    name: String,
    age: u32,
}

impl Person {
    fn get_name(&self) -> &str {
        self.name.as_str()
    }
    fn get_age(&self) -> u32 {
        self.age
    }
    fn set_name(&mut self, name: &str) {
        self.name = String::from(name);
    }
    fn set_age(&mut self, age: u32) {
        self.age = age;
    }
}

fn main() {
    let mut p1 = Person {
        name: "Boii".to_string(),
        age: 18,
    };
    println!("name: {:?}", p1.get_name());  // name: "Boii"
    println!("age:  {:?}", p1.get_age());   // age:  18
    p1.set_age(20);
    p1.set_name("Eva");
    println!("name: {:?}", p1.get_name());  // name: "Eva"
    println!("age:  {:?}", p1.get_age());   // age:  20
}

字段同名方法

Rust 允许实现与字段名同名的方法。加上括号则是调用方法,没有括号则是调用字段。

struct Person {
    name: String,
    age: u32,
}

impl Person {
    fn name(&self) -> &str {
        "method: " + self.name.as_str()
    }
    fn age(&self) -> u32 {
        "age: " + self.age
    }
}

fn main() {
    let p1 = Person {
        name: "Boii".to_string(),
        age: 18,
    };
    println!("name: {:?}", p1.name()); // name: method: "Boii"
    println!("age:  {:?}", p1.age());  // age:  age: 18
    println!("name: {:?}", p1.name);   // name: "Boii"
    println!("age:  {:?}", p1.age);    // age:  18
}

关联函数

关联函数类似类方法,第一个参数不是 self,调用的时候使用 :: 符号。

struct Person {
    name: String,
    age: u32,
}
impl Person {
    fn new(name: &str, age: u32) -> Person {
        Person {
            name: String::from(name),
            age,
        }
    }
}
fn main() {
    let p1 = Person::new("John", 19);
}