
3 结构体
变量在内存中的结构,就是通过结构体语法里定义的。包括其他什么String、Vector、各种类型,其变量的内存结构都是结构体套出来的。
说起自定义结构,很容易想起元组、数组。他们都可以快速定义一个新类型。
但是结构体的元素有命名,可以通过字段名来灵活的访问。
结构体定义与使用
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
struct关键字,以及名字,然后就是一个大括号,里面的就像json的对象。
let mut user1 = User {
active: true,
username: "noob",
email: "somethingspecial.outlook.com",
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
println!(user1.email);
struct User {
active: bool, // 看起来默认是不可变变量,但是不做数,要看实例变量可变性
username: String,
email: String,
sign_in_count: u64,
}
let mut user1 = User { // 可变实例,则所有属性都可变
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.active = false; // 可以修改 active 字段的值
在结构体中的不可变字段,照样会被移走所有权,因为mut只对变量内存而言,而非内容不可变。转移不会导致内存被释放。这种特性会导致后面我们访问该结构体变量,出现字段空。唯一的解决办法是全用不可变引用&。
元组结构体
struct User(Stirng,u8);这就是元组结构体的声明
实例化是这样let user = User(String::from("noob"),12);
所以访问的时候也只能使用索引访问user.0这样。
然后元组结构体也能解构,但是必须指名解构的结构体名:
let user = User(String::from("noob"),12);
let User(name, age) = user;
类单元结构体
声明和实例化都很奇怪,又很简洁:
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
方法
方法与函数是不同的,因为它们在结构体的上下文中被定义,并且它们第一个参数总是 self,它代表调用该方法的结构体实例。
当实例调用方法的时候,总会传入self参数,并且如果方法的参数是self:&self就传&self,需要&mut self就传&mut self,要所有权也能传self。但是如果不还所有权,那么实例可就销毁了。
Self类型是impl关键字后类型的别名
[!todo] 自动引用和解引用
这些代码是等价的:
p1.distance(&p2); (&p1).distance(&p2);
第一种写法看起来简洁得多。这种自动引用之所以成立,是因为方法有一个明确的接收者,也就是 self 的类型。给定接收者和方法名,Rust 就能明确推断出该方法是只读(&self)、可变借用(&mut self)还是获取所有权(self)。
然后自动转换成
Type::method(instance, args)。
关键就是impl <结构体名>{ 方法定义区 }
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 { // `&self` 实际上是 `self: &Self` 的缩写。
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
加上括号传参,那么就是调用方法,没有括号则是字段。
关联函数
[!summary] impl块中定义的不以self为第一参数的函数
self这个参数名都被方法锁定了,无论什么类型,只要不在第一位,则触发错误。
当然我们还可以使用Self表示别名。
impl Rectangle {
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
想要调用关联函数,必须使用类型名::关联函数名,::这个符号是命名空间的符号,和C++一样。
let sq = Rectangle::square(3);
impl块的理解
在实现具有泛型参数与生命周期的时候,impl块上需要重新标注,让编译器对块中的一些参数保持认识。
struct Wrapper<T> {
value: T,
}
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
impl<T> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
}