Skip to main content
3 结构体

3 结构体

•1 min•82 words
结构体就是自定变量的途径

变量在内存中的结构,就是通过结构体语法里定义的。包括其他什么String、Vector、各种类型,其变量的内存结构都是结构体套出来的。

说起自定义结构,很容易想起元组、数组。他们都可以快速定义一个新类型。

但是结构体的元素有命名,可以通过字段名来灵活的访问。

结构体定义与使用

Summary
struct User {
   active: bool,
   username: String,
   email: String,
   sign_in_count: u64,
}

struct关键字,以及名字,然后就是一个大括号,里面的就像json的对象。

Summary
let mut user1 = User {
  active: true,
  username: "noob",
  email: "somethingspecial.outlook.com",
  sign_in_count: 1,
};
Summary
user1.email = String::from("anotheremail@example.com");
println!(user1.email);
Summary
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 字段的值
immutable但被移走的所有权

在结构体中的不可变字段,照样会被移走所有权,因为mut只对变量内存而言,而非内容不可变。转移不会导致内存被释放。这种特性会导致后面我们访问该结构体变量,出现字段空。唯一的解决办法是全用不可变引用&。

Cite

元组结构体

有专属的名字,没有字段的名字

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;

类单元结构体

Summary

声明和实例化都很奇怪,又很简洁:

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

方法

Summary

方法与函数是不同的,因为它们在结构体的上下文中被定义,并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

当实例调用方法的时候,总会传入self参数,并且如果方法的参数是self:&self就传&self,需要&mut self就传&mut self,要所有权也能传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);
Summary

impl块的理解

rust中,方法与数据是分离式设计,所以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 }
    }
}