Skip to main content

Struct 结构体

  • 自定义的数据类型
  • 为相关联的值命名,打包组成有意义的组合。

定义 Struct

  • 使用 Struct 关键字,并且为整个 Struct 起一个有意义的名字
  • 在大括号内,为所有 字段 Field 定义名称和类型

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

实例化


fn main() {

let user = User {
username: String::from("ACE"),
email: String::from("97527027@qq.com"),
sign_in_count: 520,
active: true,
};
}

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

注意
  • 如果可变想要内容可变也是需要加 mut 关键字
  • 一旦 struct 的实例是可变的,那么实例中所有的字段都是可变的,因为 Rust 不允许声明某一部分的字段是可变的,要变都变,要么都不变。
  • struct 也可以作为函数的返回值
更新语法

fn main() {
let user1 = User {
username: String::from("ACE"),
email: String::from("1@qq.com"),
sign_in_count: 520,
active: true,
};

// old
let user2 = User {
username: String::from("SABO"),
email: String::from("2@qq.com"),
sign_in_count: user1.sign_in_count,
active: user1.active,
};

// new
let user2 = User {
username: String::from("SABO"),
email: String::from("2@qq.com"),
..user1
};
}

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

  • 这个东西类似 TypeScriptinterface type
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}

fn main() {
let username = String::from("ACE");
let email = String::from("97527027@qq.com");

let user1 = create_user(username, email);
}

fn create_user(username: String, email: String) -> User {
User {
username,
email,
sign_in_count: 1,
active: false,
}
}

元祖 Struct

  • 可定义类似 tuple 的 struct,叫做 tuple struct

    • tuple struct 整体有个名,但里面的元素没有名
    • 适用:想给整个 tuple 起名,并让它不同于其他 tuple ,而且又不需要给每个元素起名
  • 定义 tuple struct:使用 struct 关键字,后边是名字,以及里面元素的类型。

Tuple Struct
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}

black 和 origin 是不同的类型,是不同 tuple struct 的实例

Unit-Like Struct

注意:需要在某个类型上实现某个接口(trait),但是里面又没有想要存储的数据才用它 ()

Struct 的所有权

注意:Struct 里面可以存放引用,但是存储引用不使用生命周期就会报错。

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

  • 这里的字段使用了 String 而不是 &str:
    • 该 Struct 实例拥有其所有的数据
    • 只要 Struct 实例是有效的,那么里面的字段数据也是有效的

例子

fn main() {
let a = computed_fn(10, 60);

println!("{}", a);
}

fn computed_fn1(width: u32, length: u32) -> u32 {
width * length
}

turple

fn main() {
let rect = (30, 50);
let a = computed_fn(rect);

println!("{}", a);
}

fn computed_fn(dim: (u32, u32)) -> u32 {
dim.0 * dim.1
}

保留所有权计算案例
struct Rectangle {
width: u32,
length: u32,
}

fn main() {
let rect = Rectangle {
width: 100,
length: 50,
};
// 保留了 rect 所有权
println!("{}", computed_fn(&rect));
}

fn computed_fn(dim: &Rectangle) -> u32 {
dim.length * dim.width
}
计算
struct Rectangle {
width: u32,
length: u32,
}

fn main() {

let rect = Rectangle {
width: 100,
length: 30
};

let a = persion(&rect);

println!("Hello, world! {}", a);
}

fn persion(rect: &Rectangle) -> u32 {
rect.length * rect.width
}

Struct 的方法

  • 方法和函数类似:fn 关键字、名称、参数、返回值。

  • 方法和函数不同之处:

    • 方法是在 struct (或 enum、trait 对象)的上下文中定义。
    • 第一个参数是 &self,也可以是获得其所有权 或 可变借用。和其他参数一样。 表示方法被调用的 struct 实例。
    • 更良好的代码组织
/**
* impl 实现方法
*/
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

impl Rectangle {
// 第一个参数可以是&seld,也可以获得其所有权 或 可变借用。
fn persion(&self) -> u32 {
self.length * self.width
}
}

fn main() {
let rect = Rectangle {
width: 100,
length: 10,
};

let a = rect.persion();

println!("{:#?}", a);
}

方法调用的运算符

  • C/C++:object->something() 和 (*object).something() 一样
  • Rust 没有 -> 运算符
  • Rust 会自动引用或解引用
    • 在调用方法的时候就会发生这种行为
  • 调用方法的时候, Rust 根据情况自动添加 &&mut* ,以便 object 可以匹配方法的签名。
  • 下面两行代码效果相同:
    • p1.distance(&p2);
    • (&p1).distance(&p2);

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

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

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

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

fn main() {
let s = Rectangle::square(20);

let rect1 = Rectangle {
width: 100,
height: 50,
};

let rect2 = Rectangle {
width: 200,
height: 30,
};

let rect3 = Rectangle {
width: 300,
height: 40,
};

println!("{:?}", s);
println!("{:?}", rect1);
println!("{:?}", rect2);
println!("{:?}", rect3);
}

关联函数

  • 可以在 impl 块里定义不把 self 作为第一个参数的函数,它们叫关联函数(不是方法)
    • 例如:String::from()
  • 关联函数通常用于构造器(例子)
  • :: 符号
    • 关联函数
    • 模块创建的命名空间

多个 impl 块

  • 每个 struct 允许拥有多个 impl 块
old

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

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

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

new

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

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

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