一起养成写作习惯!这是我参与「掘金日新计划 · 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()
才能调用,可以更好的管理命名冲突。