《Rust 语言程序设计》笔记第四章-结构体

四、结构体

1、定义并实例化结构体

定义和实例化

定义

定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。

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

实例化

通过为每个字段指定具体值来创建这个结构体的 实例。创建一个实例需要以结构体的名字开头,接着在大括号中使用 key: value 键-值对的形式提供字段。

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

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

获取数据

使用点号,如果结构体的实例是可变的,那么还可以使用点号来改变字段值

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

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");
变量与字段同名时的字段初始化简写语法

参数名与字段名都完全相同,我们可以使用 字段初始化简写语法field init shorthand

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}
使用结构体更新语法从其他实例创建实例

.. 语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值。

下面两份代码效果相同:

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

..user1 必须放在最后,以指定其余的字段应从 user1 的相应字段中获取其值。

!!!!注意!!!

结构更新语法就像带有 = 的赋值,因为它移动了数据,所以不能再使用 user1实质上只有username字段不能用,并且可以赋值一个String值让其再次可用。),因为 user1username 字段中的 String 被移到 user2 中 。但是如果我们对 user2String都赋予新的 String 值,只对剩下的字段使用 user1 更新,则 user1 仍可以使用。因为剩下两个字段是 boolint 类型,都实现了实现 Copy trait 。这两个类型是使用克隆而不是移动。这样 user1 的所有字段的所有权都没有转移。

代码如下:

let user2 = User {
    username: String::from("another"),
    email: String::from("another@example.com"),
    ..user1
};
// 此时只有 user1.username 不可用
println!("user1  {} {} {}",user1.email,user1.sign_in_count,user1.active);
user1.username = String::from("username"); // 赋值之后, username 再次可用。
元组结构体

元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型

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

let black = Color(0, 1, 2);
let origin = Point(0, 1, 2);
println!("black  {} {} {}",black.0,black.1,black.2);
println!("origin  {} {} {}",origin.0,origin.1,origin.2);

注意 blackorigin 值的类型不同,因为它们是不同的元组结构体的实例。

也可以使用 . 后跟索引来访问单独的值

类单元结构体

一个没有任何字段的结构体!它们被称为 类单元结构体unit-like structs)因为它们类似于 (),即“元组类型”一节中提到的 unit 类型。

类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。

2、通过派生 trait 增加实用功能

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

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

    println!("rect1 is {:?}", rect1);
}

增加属性来派生 Debug trait,并使用调试格式打印 Rectangle 实例

另一种使用 Debug 格式打印数值的方法是使用 dbg!宏。dbg!接收一个表达式的所有权,打印出你代码中 dbg! 宏调用的文件和行号,以及该表达式的结果值,并返回该值的所有权

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

fn main() {
    let scale = 2;
    let rect1 = Rectangle {
        width: dbg!(30 * scale),
        height: 50,
    };

    dbg!(&rect1);
}

3、方法语句

定义方法
#[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()
    );
}

impl + 结构体 块,方法签名的第一个参数必须是 self,这里只需要读取数据,所以使用了引用,&self 相当于 self : &self,这里 Rust 允许只用 self 缩写。

带有更多参数的方法

这里实现一个 can_hold 方法,传入另一个 Rectangle ,比较 self 是否能完全包含这个长方形。

#![allow(unused)]
fn main() {
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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
    }
}
    
    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));
}
关联函数

所有在 impl 块中定义的函数被称为 关联函数associated functions),因为它们与 impl 后面命名的类型相关。我们可以定义不以 self 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例

不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。

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

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}
let sq = Rectangle::square(3);

使用结构体名和 :: 语法来调用这个关联函数

多个 impl 块

每个结构体都允许拥有多个 impl 块。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值