结构体
定义并实例化结构体
使用struct
,像其它任何语言一样,声明所有数据的类型和名称(这里你注意到,声明的最后也有一个逗号)
strcut User {
username:String,
email:String,
sign_in_count:u64,
active:bool,
}
创建一个结构体实例时,通过下面的语法进行创建。每一项都是数据名称:实例数据
。注意可以不按照声明顺序排列:
let mut user1 = User {
username:String::from("a@xx.com"),
email:String::from("xx"),
sign_in_count:1,
active:true,
};
访问结构体的成员时,使用.
。就如在其它任何语言中一样
user1.active = false;
下面提供几种快捷的初始化实例的方法
在变量名与字段名相同时使用简化版字段初始化方法
例子:
fn build_user(email:String,username:String) -> User {
User {
email,
username,
avtive:true,
sign_in_count:1,
}
}
由于字段名email
和我们用于初始化该字段的变量名email
相同,我们可以将email:email
简写成email
使用结构体更新语法根据其它实例创建新实例
例子:
let user2 = User {
username:String::from("a@xx.com"),
email:String::from("xx"),
..user1
};
上面等同于使用以下代码,根据user1
的数据创建user2
let user2 = User {
username:String::from("a@xx.com"),
email:String::from("xx"),
sign_in_count:user1.sign_in_count,
active:user1.active,
};
使用不需要对字段命名的元组结构体来创建不同类型
一般来说,当你想要给元组赋予名字,并使其有区别于其它有同样定义的元组时,使用元组结构体。其语法看上去就是为元组命了个名
struct Color(i32,i32,i32);
let black = Color(0,0,0);
元组结构体实例的行为与元组完全一致
没有任何字段的结构体
允许创建不包含任何字段的空结构体,通常用于在某些类型上实现trait,却不需要在该类型上存储数据时
使用结构体
我们以一个长方形结构体为例
struct Rectangle {
width:u32,
height:u32,
}
fn main {
let rect1 = Rectangle {width:30,height:20};
}
通过派生trait增加实用功能
假如我们尝试
println!("rect1 is {}",rect1);
会报错
|
8 | println!("rect1 is {}",rect1);
| ^^^^^ `Rectangle` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
该报错的help指示我们,实现std::fmt::Display
,这个trait才可以。
后面又建议我们使用{:?}
,这是使用Debug的格式化输出。我们改一下:
println!("rect1 is {:?}",rect1);
又报错了
|
8 | println!("rect1 is {:?}",rect1);
| ^^^^^ `Rectangle` cannot be formatted using `{:?}`
|
= help: the trait `Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `Rectangle` with `#[derive(Debug)]`
|
1 | #[derive(Debug)]
|
我们直接听从意见,在结构体顶以前加#[derive(Debug)]
注解,修改程序为
#[derive(Debug)]
struct Rectangle {
width:u32,
height:u32,
}
fn main() {
let rect1 = Rectangle {width:30,height:20};
println!("rect1 is {:?}",rect1);
}
最终程序会打印:rect1 is Rectangle { width: 30, height: 20 }
,这可以用于调试
方法
方法与函数基本上类似,区别在于:
- 方法定义在某个结构体的上下文中
- 方法的第一个参数永远是
self
定义方法
给我们的长方体定义一个计算面积的方法
impl Rectangle {
fn area(&self) -> u32 {
self.width*self.height
}
}
- 方法的定义位于impl块中
- 当然,每个结构体都可以拥有多个impl块
- 第一个参数是
&self
- 正是因为位于
impl Rectangle { }
中,&self
才能被推导为rectangle:&Rectangle
- 若方法要获得所有权,使用
self
;若要修改调用者的数据,使用&mut self
;不过我们这里只需要读取,所以使用了&self
- 正是因为位于
- 使用方法时,
rect1.area()
。此时rect1
传递给参数&self
关联函数
函数与结构体直接关联,定义在impl块中,但是不接受self作为参数
impl Rectangle {
fn square(size:u32) -> Rectangle {
Rectangle {width:size,height:size}
}
}
使用时,在类型后面加::
。比如let sq = Retangle::square(3);