Rust 之 struct 基础

1,579 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

定义一个struct

先看代码:

struct User {
    name: String,
    age: u8,
}

上面的代码,定义了一个struct,名字是User

字段有两个:

  • name String 类型
  • age u8 类型

一个struct的定义,就相当于给整个系统引进了一个新的类型,这个类型的名字就是struct的名字:User

从这之后,你的代码就可以用这个新的类型来存储东西了。

实例化

先看代码:

let xiaoming = User {
       age: 22,
       name: String::from("xiaoming"),
};

这一段太简单,没啥可说的,不过对于新手来说,需要注意,不用按照字段定义的顺序来实例化。

比如说,定义的时候,name在前面,age在后面。

实例化的时候,age在前面,name在后面。

其实就是struct的好处,用字段名,而非顺序来标记字段。

获取字段的值

代码:

println!("{}的年龄是{}",xiaoming.name, xiaoming.age)

设置字段的值

xiaoming.age = 23;

直接搞这一句,肯定是报错:

cannot assign to xiaoming.age, as xiaoming is not declared as mutable。

意思就是,xiaoming 这个变量没有被声明成 mutable,也就是可变的。

xiaoming 这个结构体实例变量,必须要是mut的,我们才能更改其中某个字段的值。

试试:

let mut xiaoming = User {
       age: 22,
       name: String::from("xiaoming"),
};
xiaoming.age = 23;

行了这回没报错了。

需要注意的是,不能对某个字段声明 mut,只能对整体声明 mut

使用函数初始化struct

fn create_user(age: u8, name: String) -> User {
    User {
        age: age,
        name: name,
    }
}

注意,User 的最后的花括号,没有接;, 表明这个是要return出去。

字段初始化简写

看上面,函数的字段和struct字段重名了,所以rust可以这样来简写:

fn create_user(age: u8, name: String) -> User {
    User { age, name }
}

使用一个struct 变量初始化另一个struct 变量

这是很经常遇见的场景,比如说,小明和小红,所有的属性都一样,就是小红小一岁。

此时如果按照常规来写:

let xiaohong = User {
    name: xiaoming.name,
    ...
    ...
    ...
    ...
    age: 21,
};

中间的省略号,表示,把所有的小明的字段都复制过来,然后age给一个21。

这么写有点费手,费键盘,所以rust

let xiaohong = User {
        age: 22,
        ..xiaoming
};

两个.加你想复制的变量,就行了。

不要字段名的struct定义

有时候,为了方便,我们不要具体的字段名, 只需要struct名:

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


我们看到这两个struct定义的时候,都没有给字段名称,只有字段类型。

因为这是一组性质相同的数据,就跟数组差不多。

跟数组有区别的就是,Color 和 Position 是两个类型。

不能互相赋值啥的。

如何来实例化呢:

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

一般都是整体使用的,如果想获得其中某一个字段的值,就用.加上序号就行:

struct Color(i32, i32, i32);
fn main() {
    let c = Color(1, 2, 3);
    println!("{}", c.0);
}

给struct加上逻辑

rust里面可以给一个struct加上两种不同的逻辑:

  • 方法(method)
  • 关联函数(associated funciton)

方法

方法和普通的函数很像:
fn开头、 函数名、函数参数、函数返回值。

有区别的就是,方法,必须存在一个struct上下文中,使用impl关键字加上struct名来开启关于方法的定义:

struct User{
    name : String,
    age : u8,
}
impl User {
    fn show_info(&self) {
        println!("{}的年龄是{}", self.name, self.age)
    }
}

这里面注意show_info的括号里的第一个参数,这很奇怪。

其实这是一个缩写:

self : &User

而且这玩意的变量名只能是 self

使用的时候这样:

    let xiaoming = User {
        name : String::from("xiaoming"),
        age : 22,
    };
    xiaoming.show_info();

你会发现,我们调用方法的时候,没有传参数,那么self这个参数是干啥?

其实,这只是一种语法糖而已,实际上,调用show_info函数的时候,自动把xiaoming这个变量传递进去了而已。

关联函数

关联函数和方法的定义几乎一样,如果你把方法的第一个参数,不传&self, 那么这东西就称之为关联函数。

impl User {
    fn show_info(&self) {
        println!("{}的年龄是{}", self.name, self.age);
    } // 这个是方法
    fn create_user(name: String, age: u8) -> User {
        User { name, age }
    }
}

我们从这里可以看出,关联函数有点类似于别的语言里的静态方法,就是没有跟某一个具体的对象有关系的方法。

这种东西一般用来创建一个对象,比如说上面的create_user。

还比如说,String::from 这个函数,其实也是一个关联函数,用来初始化用的。

这玩意的好处就是,给create_user加了一个作用域,你只有:

User::create_user()

才能调用,可以更好的管理命名冲突。