Rust结构体和枚举

2023-09-18 18:48:29

结构体

struct,或者 structure,是一个自定义数据类型,允许你包装和命名多个相关的值,从而形成一个有意义的组合。

声明

定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 字段field)。

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

fn main() {}

相比较元组而言,元组自身并没有名字,每个数据也没有名字,只能按顺序来指定或访问实例中的值。元组只是多个值的集合,而结构体和枚举可以拥有实现(方法)。Rust没有类这个概念,枚举和结构体是Rust面向对象的实体基础。

结构体默认是不可变的,我们通过声明let mut来声明一个可变结构体的变量。

当参数名与字段名都完全相同,我们可以使用 字段初始化简写语法,这个类似JavaScript ES6里的写法。

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

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

fn main() {
    let user1 = build_user(
        String::from("someone@example.com"),
        String::from("someusername123"),
    );
}

使用旧实例的大部分值但改变其部分值来创建一个新的结构体实例通常是很有用的。这可以通过 结构体更新语法struct update syntax)实现:

fn main() {
    // --snip--

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

虽然JavaScript中也有类似的展开,但是很不幸如果按照上述写法,user2的值将和user1完全一样,因为email会被user1的展开所覆盖,如果想要类似的效果,需要把email字段写到user1的展开后面。

也可以定义与元组(在第三章讨论过)类似的结构体,称为 元组结构体tuple structs)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的。元组结构体实例类似于元组,你可以将它们解构为单独的部分,也可以使用 . 后跟索引来访问单独的值。

我们也可以定义一个没有任何字段的结构体!它们被称为 类单元结构体unit-like structs)因为它们类似于 (),即“元组类型”一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。

方法

方法(method)与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文),并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

定义和实现方法

为了使函数定义于 Rectangle 的上下文中,我们开始了一个 impl 块(implimplementation 的缩写),这个 impl 块中的所有内容都将与 Rectangle 类型相关联。接着将 area 函数移动到 impl 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 self

关联函数

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

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

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

fn main() {
    let sq = Rectangle::square(3);
}

关键字 Self 在函数的返回类型中代指在 impl 关键字后出现的类型,在这里是 Rectangle

使用结构体名和 :: 语法来调用这个关联函数:比如 let sq = Rectangle::square(3);。这个函数位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

impl块

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

 

枚举

枚举类似结构体,也可以作为值的集合。但是从语义上讲,结构体的字段组成了这个结构体描述的实体,而枚举是多个选项的集合,每个选项是一种实体,而枚举值只能是其中的一个选项。

声明

枚举使用enum声明枚举类型,枚举类型的可能选项是枚举的成员值。例如:

enum IpAddrKind {
    V4,
    V6,
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。

关联值

枚举值可以和值进行关联,这个和Swift是类似的。

fn main() {
    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));
}

上面的例子里,我们将IP枚举值和具体的IP值进行了关联。甚至于说,不同的枚举值可以和不同类型、不同数量的值进行关联:

fn main() {
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));
}

方法

结构体和枚举还有另一个相似点:就像可以使用 impl 来为结构体定义方法那样,也可以在枚举上定义方法。

match控制流

针对枚举值,在其他语言里我们常常使用类似Swift里的switch、case,以及kotlin里的where来做类似的匹配和处理。Rust里使用的是match。

绑定值的模式

因为枚举可以关联值,所以也可以在模式匹配的时候,绑定关联的值:

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        }
    }
}

fn main() {
    value_in_cents(Coin::Quarter(UsState::Alaska));
}

穷尽匹配

match匹配必须覆盖全部的情况,否则编译器会报错。对于不想处理的情况,可以使用other作为通配模式。如果我们甚至都不想使用这个通配的值,我们可以用“_”作为other的代替来占位。

if let简洁控制流

我们常常需要从Option<T>获得值,例如:

fn main() {
    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("The maximum is configured to be {}", max),
        _ => (),
    }
}

这种情况,我们可以使用if let来简化:

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {}", max);
    }
}

当然也不仅限于Option<T>,只要是需要匹配某一模式而忽略所有其他值的情况时,都可以使用。

参考:

1.使用结构体组织相关联的数据

2.枚举和模式匹配

更多推荐

一文读懂SQL的增删改查(基础教程)

前言一、一些最重要的SQL命令二、查询(SELECT)1、查询所有列2、查询指定列3、查询并去重(DISTINCT)4、按条件查询where5、SQLAND&OR运算符6、SQLORDERBY关键字7、SQLLIMIT关键字8、SQLLIKE操作符9、SQLIN操作符9、SQLBETWEEN操作符三、插入(INSERT

黑马JVM总结(十七)

(1)G1_简介下面介绍一种Grabageone的垃圾回收器,在jdk9的时候称为默认的回收器,废除了之前的CMS垃圾回收器,它的内部也是并发的垃圾回收器我们可以想到堆内存过大,肯定会导致回收速度变慢,因为要涉及到对象的复制、标记,内存过大,对速度会产生影响,划分为小的区域进行管理,可以进行一些优化,标记和复制的速度在

GaussDB之应用无损透明(ALT)

1.背景GaussDB作为一款企业级分布式数据库,提供了“同城跨AZ双活、两地三中心、双集群强一致”等极致的高可用容灾能力。当某个数据库节点由于故障无法对外提供服务时,为了继续保证数据库服务的可用性,JDBC驱动会将业务后续的数据库连接请求发送到其它可用节点上。但故障发生后,已经与故障节点建立会话的连接无法自动切换到可

手撕排序之堆排序

一、概念:什么是逻辑结构、物理结构?逻辑结构:是我们自己想象出来的,就像内存中不存在一个真正的树物理结构(存储结构):实际上在内存中存储的形式。堆的逻辑结构是一颗完全二叉树堆的物理结构是一个数组之前讲过二叉树可以用两种结构进行表示。第一种就是链式结构,将一个一个结点进行链接。第二种就是用数组表示。数组表示意味着我们就是

Godot配置C#语言编写脚本(使用VSCode作为外部编辑器)

文章目录Godot部分查看VSCode的所在位置配置外部编辑器配置VSCode编写脚本中文注释其他文章字符编码Godot部分打开编辑器-编辑器设置;查看VSCode的所在位置右键单击你的VScode快捷方式,选择属性。这里的目标就是你的VSCode所在的位置。配置外部编辑器在编辑器设置里找到.NET-编辑器-Exter

全国职业技能大赛云计算--高职组赛题卷②(容器云)

全国职业技能大赛云计算--高职组赛题卷②(容器云)第二场次题目:容器云平台部署与运维任务1DockerCE及私有仓库安装任务(5分)任务2基于容器的web应用系统部署任务(15分)任务3基于容器的持续集成部署任务(15分)任务4Kubernetes容器云平台部署与运维(15分,本任务只公布考试范围,不公布赛题)需要环境

全国职业技能大赛云计算--高职组赛题卷⑤(私有云)

全国职业技能大赛云计算--高职组赛题卷⑤(私有云)第一场次题目:OpenStack平台部署与运维任务1基础运维任务(5分)任务2OpenStack搭建任务(15分)任务3OpenStack云平台运维(15分)任务4OpenStack云平台运维开发(15分,本任务只公布考试范围,不公布赛题)需要环境私信博主!!!第一场次

人工智能是否有风险

批判性思考人工智能这一挑战经常由人工智能的支持者提出:如果一个机器人足够聪明,与自然人完全没有区别,那么它拥有公民权利难道不是道德和正确的吗?“杀死”这样的生物岂不是有错?我的回答是,这样的生物不能被赋予公民权利,并且消除它本身并没有错。回答通常是这样的:“这难道不是人类自我在说话吗?这就是基于基质的歧视,不是吗?”不

虹科案例 | Zuellig Pharma和ELPRO通过符合GDP标准的温度监测和高效的温度数据管理为未来发展奠定基础

在本案例研究中,您将了解ZuelligPharma实施了温度监测解决方案,以一致的数据结构获取各国和各种运输方式的数据;通过将温度数据上传到其数据库管理系统,显著提高了其效率;并建立了为未来管理决策提供数据增值使用的基础。项目合作伙伴ZuelligPharma是亚洲最大的医疗保健服务集团之一。该公司提供世界级的分销、数

【入门篇】ClickHouse最优秀的开源列式存储数据库

文章目录一、什么是ClickHouse?OLAP场景的关键特征列式数据库更适合OLAP场景的原因输入/输出CPU1.1ClickHouse的定义与发展历程1.2ClickHouse的版本介绍二、ClickHouse的主要特性2.1高性能的列式存储2.2实时的分析查询2.3高度可扩展性2.4数据压缩2.5SQL支持2.6

[npm] npx 介绍与使用说明

[npm]npx介绍与使用说明npm的由来npx是什么?npx特点npx的特点项目安装包的使用全局安装包的避免指定工具包版本--no-install参数和--ignore-existing参数使用不同版本的node-p参数-c参数实战应用执行GitHub源码npm的由来说到npm就离不开社区文化,那什么是社区文化?社区

热文推荐