结构或结构体,是一种自定义数据类型,它允许我们命名多个相关的值并将它们组成一个有机的结合体。
1、定义并实例化结构体:
和元组一样,结构体中的数据可以拥有不同的类型,不同的是,结构体需要给每个数据赋予名字以便于清楚地表达它们的意义。
关键字struct
被用来定义并命名结构体。一个良好的结构体名称应当能够反映出自身数据组合的意义。需要在随后的花括号中声明所有数据的名字及类型,这些数据也被称为字段。
示例:
rust
struct User{
username:String,
email:String,
sign_in_count:u64,
active:bool,
}
在定义好结构体之后,需要为每个字段赋值来创建结构体实例,结构体实例赋值的顺序可以不按照所定义的顺序。
示例:
rust
let User1 = User{
email:String::from("xxxx@xx.com"),
username:String::from("xxxx"),
active:true,
sign_in_count:1,
}
在创建好结构体实例之后,可以通过点号来访问实例中的特定的字段,例如需要访问User1中的名字,可以使用User1.username
来获取。同样也可以通过点号来修改可变实例中相应的字段。
注:一旦实例可变,实例中所有的字段都是可变的,Rust不允许单独声明某一部分字段的可变性。
(1)、在变量名与字段名相同时使用简化版的字段初始化方法:
当函数的参数与结构体字段拥有完全一致的名称,可以使用名为字段初始化简写的语法来构建函数,这种语法并不会改变函数的行为。
示例:
rust
fn build_user(email:String,username:String)->User{
User{
email,
username,
active:true,
sign_in_count:1,
}
}
(2)、使用结构体更新语法根据其他实例创建新实例:
许多新实例的创建,除了修改小部分的字段,其他部分与旧实例完全相同,就可以使用结构体更新语法来快速实现新实例的创建。
示例:
rust
let User1 = User{
email:String::from("xxxx@xx.com"),
username:String::from("xxxx"),
active:true,
sign_in_count:1,
}
let User2 = User{
email:String::from("xxxx@xx.com"),
username:String::from("xxxx"),
active:user1.active,
sign_in_count:user1.sign_in_count,
}
为了简化代码量,可以使用更少的代码完成创建。
示例:
rust
let User1 = User{
email:String::from("xxxx@xx.com"),
username:String::from("xxxx"),
active:true,
sign_in_count:1,
}
let User2 = User{
email:String::from("xxxx@xx.com"),
username:String::from("xxxx"),
..user1
}
(3)、使用不需要对字段命名的元组结构来创建不同的类型:
元组结构体:使用一种类似于元组的方式来定义结构体。
元组结构体同样拥有用于表明自身含义的名称,但是无须在声明它时对其字段进行命名,仅保留字段的类型即可。当需要给元组赋予名字,并使其区别于其他拥有同样定义的元组时,可以使用元组结构体。
定义元组结构体时,依旧使用struct
关键字开头,并由结构体名称及元组中的类型定义组成。
示例:
rust
struct Color(i32,i32,i32);
struct Point(i32,i32,i32);
let black = Color(0,0,0);
let origin = Point(0,0,0);
(4)、没有任何字段的空结构体:
Rust允许创建没有任何字段的结构体。因为这种结构体与空元组十分相似,也被称为空结构体。当要在某些类型上实现一个trait,但是不需要该类型储存任何数据时,可以使用空结构体。
2、方法:
方法与函数十分相似:都是用fn
关键字以及一个名称来进行声明;都可以拥有参数和返回值;都包含了一段在调试时执行的代码。但是方法和函数是两个不同的概念,因为方法总是被定义在某个结构体(或者枚举类型、trait对象)的上下文中,并且它们的第一个参数永远是self,用于指代调用该方法的结构体实例。
(1)、定义方法:
示例:
rust
#[derive(Debug)]
struct Rectangle{
width:u32,
height:u32,
}
fn main(){
let rect1 = Rectangle{width:30,height:50};
println!("rect1 is {:?}",rect1);
println!("The area of the rectangle is {} square pixels.",area(&rect1));
}
fn area(rectangle:&Rectangle) -> u32{
rectangle.width*rectangle.height
}
使用方法进行改写:
rust
#[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!("rect1 is {:?}",rect1);
println!("The area of the rectangle is {} square pixels.",rect1.area());
}
为了在Rectangle的上下文环境中定义这个函数,需要将area函数移动到一个由impl关键字起始的代码块中,并把签名中的第一个参数和函数中使用该参数的地方改写成self。此外,还需要把main函数中调用area函数的地方,用方法调用的语法进行改写。方法调用是通过在实例后面加上点号,并跟上方法名、括号及可能的参数来实现的。
(2)、关联函数:
除了方法外,Rust还允许定义不用接收self作为参数的函数。由于这类函数与结构体相互关联,故称之为关联函数。
关联函数常常被用作构造器来返回一个结构体的新实例。例如,可以编写一个接收一维参数的关联函数,它会将输入的参数同时作用长度与宽度来构造正方形的Rectangle实例。
示例:
rust
impl Rectangle{
fn square(size:u32) -> Rectangle{
Rectangle{width:size,height:size}
}
}
在类型名称后面添加::
来调用关联函数,例如let sq = Rectangle::square(3);