结构体需要命名各部分数据以便能清楚的表明其值的意义
结构体比元组更灵活:不需要依赖顺序来指定/访问实例中的值
// 整个实例可变;Rust 不允许只将某个字段标记为可变
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
let mut user1 = User {
// 使用自身拥有所有权的 String 类型,而不是 &str 字符串 slice 类型
// 想要这个结构体拥有它所有的数据
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("[email protected]");
变量与字段同名时的间写
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
依据其他实例创建实例
fn main() {
// --snip--
// 设置一个新的 email 值,其余值来自 user1 变量中实例的字段
let user2 = User {
email: String::from("[email protected]"),
// 创建 user2 后不能再使用 user1
// user1 的 username 字段中的 String 被移到 user2
// 如果给 user2 的 email 和 username 赋予新 String 值,只使用 user1 的 active 和 sign_in_count 值,
// 则user1 在创建 user2 后仍然有效
..user1
};
}
没有具体的字段名,只有字段的类型,但有名称,注意元组没名称
元组结构体实例类似于元组:
(1)可以将其解构为单独部分
(2)可以使用 . 后跟索引来访问单独的值
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
要在某个类型上实现 trait 但不需要在类型中存储数据
不需要花括号或圆括号
用于特性trait
struct AlwaysEqual;
fn main() {
// 在 subject 变量中获得 AlwaysEqual 的实例
let subject = AlwaysEqual;
}
错误案例:missing lifetime specifier,丢失生命期标识符
struct User {
active: bool,
username: &str,
email: &str,
sign_in_count: u64,
}
fn main() {
let user1 = User {
// 在结构体中存储一个引用而不指定生命周期,无效
email: "[email protected]",
username: "someusername123",
active: true,
sign_in_count: 1,
};
}
元组缺点:其他模块使用方这么知道第一个元素是宽还是长
fn main() {
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
area(rect1)
);
}
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
结构体解决了上述问题
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
area(&rect1)
);
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
Debug trait
// 为结构体显式选择打印调试信息的功能
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:?}", rect1); // 平铺打印
println!("rect1 is {:#?}", rect1); // 类似json格式化的打印
// 更优雅的做法
// 不希望 dbg! 拥有 rect1 的所有权
dbg!(&rect1);
}
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// impl 块中的所有内容都将与 Rectangle 类型相关联
// Self 类型是 impl 块的类型的别名,注意Self和self区别
impl Rectangle {
// 在结构体的上下文中被定义
// &self 实际上是 self: &Self 的缩写
// 方法可以选择获得 self 的所有权,这里一看就是没有选择
// 如果想在方法中改变调用方法的实例,需将第一个参数改为 &mut self
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
impl Rectangle {
// 通常,与字段同名的方法将被定义为只返回字段中的值
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
// 在同名方法中使用一个字段来达到任何目的
// 在 rect1.width 后面加上括号时,Rust 知道指的是方法 width
// 当不使用圆括号时,Rust 知道指的是字段 width
if rect1.width() {
println!("The rectangle has a nonzero width; it is {}", rect1.width);
}
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
// self 是否能包裹other
// 获取另一个 Rectangle 的不可变借用作为参数
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
所有在 impl 块中定义的函数被称为关联函数(associated function)
定义不以 self 为第一参数的关联函数不叫方法
它们不作用于一个结构体的实例,而是直接作用于类型上,比如 String::from 函数,在 String 类型上定义
关联函数经常被用作返回一个结构体新实例的构造函数
impl Rectangle {
// 定义一个正方形
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
使用结构体名和 :: 语法来调用这个关联函数:
let sq = Rectangle::square(3);
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
C/C++ 语言中,有两个不同的运算符来调用方法:
(1). 直接在对象上调用方法,
(2)-> 在一个对象的指针上调用方法,先解引用(dereference)指针
Rust 并没有一个与 -> 等效的运算符
Rust 有一个叫 自动引用和解引用(automatic referencing and dereferencing)的功能(方法调用中有用到):
当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 *,以便使 object 与方法签名匹配
等价代码:
p1.distance(&p2);
(&p1).distance(&p2);
这种自动引用的行为之所以有效,是因为方法有一个明确的接收者:self 的类型。
在给出接收者和方法名的前提下,Rust 可以明确地计算出方法:
仅读取(&self),做出修改(&mut self)或者是获取所有权(self)
Rust 对方法接收者的隐式借用,让使用所有权的人更少顾虑