Rust的结构体

我爱海鲸 2024-01-28 14:29:02 rust学习

简介struct

1、定义并实例化struct

什么是 struct

struct,结构体一自定义的数据类型

   为相关联的值命名,打包=>有意义的组合

定义struct

使用struct关键字,并为整个struct命名

在花括号内,为所有字段(Field)定义名称和类型

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

实例化 struct

   想要使用struct,需要创建struct的实例:

      为每个字段指定具体值

      无需按声明的顺序进行指定

fn main() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
}

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

注意我们在赋值的时候需要把每一个值都进行赋值,不然就会报错的

取得struct 里面的某个值

使用点标记法

 let mut user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    user1.email = String::from("anotheremail@example.com");

一旦 struct的实例是可变的,那么实例中所有的字段都是可变的,rust不允许struct中的字段部分可变部分不可变

struct作为函数的返回值

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

字段初始化简写

当字段名与字段值对应变量名相同时,就可以使用字段初始化简写的方式:

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

struct 更新语法

当你想基于某个struct实例来创建一个新实例的时候,可以使用struct更新语法:

    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        active: user1.active,
        sign_in_count: user1.sign_in_count,
    };

    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        ..user1
    };

这种写法就表示user2中还没有被赋值的值就和user1的值

Tuple struct(元祖结构体)

可定义类似tuple 的 struct,叫做tuple struct

Tuple struct整体有个名,但里面的元素没有名

适用:想给整个tuple起名,并让它不同于其它tuple,而且又不需要给每个元素起名

定义tuple struct:使用struct关键字,后边是名字,以及里面元素的类型

    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32);
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

black和 origin是不同的类型,是不同 tuple struct的实例。

Unit-Like Struct(没有任何字段)

可以定义没有任何字段的struct,叫做Unit-Like struct(因为与(),单元类型类似)

适用于需要在某个类型上实现某个trait,但是在里面又没有想要存储的数据

struct 数据的所有权

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

这里的字段使用了string 而不是&str:

   该struct实例拥有其所有的数据

   只要struct实例是有效的,那么里面的字段数据也是有效的

struct 里也可以存放引用,但这需要使用生命周期(以后讲)。

生命周期保证只要struct实例是有效的,那么里面的引用也是有效的。

如果struct里面存储引用,而不使用生命周期,就会报错(例子)。

struct User {
    username: &str,
    email: &str,
    sign_in_count: u64,
    active: bool,
}

上面的例子中username和email就会报错,提示缺少生命周期

2、struct的例子

计算长方形面积

创建项目

cargo new demo 创建项目

code demo 使用vscode打开

fn main() {
    let w = 5;
    let l = 6;

    println!("{}",area(w, l));
}

fn area(width:usize,length:usize)->usize {
    width * length
}

这就是我们的第一个版本的计算长方形的面积,看简单吧

但是还是有问题的,因为长方形的长和宽是有关联的,只有它们组合在一起才是有意义的

现在我们在来写第二版

fn main() {
    let rect = (30,50);
    println!("{}",area(rect));
}

fn area(dim:(usize,usize))->usize {
    dim.0 * dim.1
}

现在我们把这两个参数组合在一起了,但是它的可读性却变差了,因为我们不知道哪一个是长哪一个是宽了。

现在我们在来写第三版

fn main() {
    let rect = Rectangle{
        width:50,
        length:30
    };
    println!("{}",area(&rect));
    println!("{:#?}",rect)
}

#[derive(Debug)]
struct Rectangle {
    width:usize,
    length:usize
}

fn area(dim:&Rectangle)->usize {
    dim.width * dim.length
}

上面就完成了它可读性的要求又有组合关联的含义

涉及到的内容

std:fmt:Display // 实现调试trait

std::fmt::Debug // 实现调试trait

#[derive(Debug)] // 实现调试trait

{:?} // 打印输出

{:#?} // 打印格式化输出

3、struct 的方法

方法和函数类似: fn关键字、名称、参数、返回值

方法与函数不同之处:

   方法是在struct(或enum、trait对象)的上下文中定义

   第一个参数是self,表示方法被调用的struct实例

定义方法

fn main() {
    let rect = Rectangle{
        width:50,
        length:30
    };
    println!("{}",rect.area());
    println!("{:#?}",rect)
}

impl Rectangle {
    fn area(&self)->usize {
        self.width * self.length
    }
}

#[derive(Debug)]
struct Rectangle {
    width:usize,
    length:usize
}


在impl块里定义方法

方法的第一个参数可以是&self,也可以获得其所有权或可变借用。和其他参数一样。

更良好的代码组织。

方法调用的运算符

C/C++: object->something()和(*object).something()一样

Rust没有->运算符

Rust会自动引用或解引用

   在调用方法时就会发生这种行为

在调用方法时,Rust根据情况自动添加&、&mut或*,以便object可以匹配方法的签名。

下面两行代码效果相同:

   p1.distance(&p2);

   (&p1).distance(&p2);

方法的调用

fn main() {
    let rect1 = Rectangle{
        width:50,
        length:30
    };
    let rect2 = Rectangle{
        width:40,
        length:20
    };
    let rect3 = Rectangle{
        width:55,
        length:35
    };
    println!("{}",rect1.can_hold(rect2));
    println!("{}",rect1.can_hold(rect3));
}

impl Rectangle {
    fn area(&self)->usize {
        self.width * self.length
    }
    fn can_hold(&self,other:Rectangle)->bool {
        self.width > other.width && self.length > other.length
    }
}

#[derive(Debug)]
struct Rectangle {
    width:usize,
    length:usize
}


关联函数

可以在 impl块里定义不把self 作为第一个参数的函数,它们叫关联函数(不是方法)

例如: String:from()

关联函数通常用于构造器(例子)

fn main() {
    // 调用关联函数
    let s = Rectangle::square(20);
    println!("{:#?}",s);

    let rect1 = Rectangle{
        width:50,
        length:30
    };
    let rect2 = Rectangle{
        width:40,
        length:20
    };
    let rect3 = Rectangle{
        width:55,
        length:35
    };
    println!("{}",rect1.can_hold(rect2));
    println!("{}",rect1.can_hold(rect3));
}

impl Rectangle {
    fn area(&self)->usize {
        self.width * self.length
    }
    fn can_hold(&self,other:Rectangle)->bool {
        self.width > other.width && self.length > other.length
    }
    // 关联函数
    fn square(size:usize)->Rectangle{
        Rectangle{
            width:size,
            length:size
        }
    }
}

#[derive(Debug)]
struct Rectangle {
    width:usize,
    length:usize
}


符号::

   关联函数

   模块创建的命名空间

多个impl块

   每个struct允许拥有多个impl块(例子)

fn main() {
    // 调用关联函数
    let s = Rectangle::square(20);
    println!("{:#?}",s);

    let rect1 = Rectangle{
        width:50,
        length:30
    };
    let rect2 = Rectangle{
        width:40,
        length:20
    };
    let rect3 = Rectangle{
        width:55,
        length:35
    };
    println!("{}",rect1.can_hold(rect2));
    println!("{}",rect1.can_hold(rect3));
}

impl Rectangle {
    fn area(&self)->usize {
        self.width * self.length
    }
}

impl Rectangle {
    fn can_hold(&self,other:Rectangle)->bool {
        self.width > other.width && self.length > other.length
    }
    // 关联函数
    fn square(size:usize)->Rectangle{
        Rectangle{
            width:size,
            length:size
        }
    }
}

#[derive(Debug)]
struct Rectangle {
    width:usize,
    length:usize
}


struct的内容我们就先搞这么多。

你好:我的2025