rust笔记-tuple和结构体

元组 tuple

元组是固定数量的元素的集合,每个元素都有一个类型,类型可以不同也可以相同。可以用下面方法创建一个元组:

fn main () {
    let tup: (u32, u8) = (10, 1);
    let tup1 = (100, 1.3);
    println!("tup = {:?}", tup);
    println!("tup1 = {:?}", tup1);
}

元组可以作为函数的参数和返回值,也可以作为结构体的字段。可以使用let语句来解构元组,也可以使用_来忽略不需要的元素。也可以使用.操作符来获取元组的值。

fn main () {
    let tup = (10, 1);
    println!("{}, {}", tup.0, tup.1);
    let (x, y) = tup;
    println!("x = {}, y = {}", x, y);
}

使用元组作为函数的返回值:

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度

    (s, length)
}

结构体 struct

跟其他语言一样,Rust 也提供了结构体来封装数据。结构体是类似 C 语言的结构体的一个概念,不过 Rust 中的结构体是类型安全的。

下面定义一个book的结构体:

struct Book {
    title: String,
    author: String,
    publish_date: i32,
    price: f32,
}

结构体定义了 title、author、publish_date、price 字段,每个字段都有类型,类型可以是基本类型,也可以是结构体。

创建结构体实例

let book = Book.create("Rust 入门", "张三", 2019, 100.0);
let book1 = Book {
    title: "Rust 入门".to_string(),
    author: "张三".to_string(),
    publish_date: 2019,
    price: 100.0,
};

结构体实例的创建方式有两种,一种是使用字段名,另一种是使用字段名和字段类型,这两种方式都可以创建结构

访问结构体字段

struct Book {
    title: String,
    author: String,
    publish_date: i32,
    price: f32,
}

fn main() {
    let book = Book {
        title: "Rust 入门".to_string(),
        author: "张三".to_string(),
        publish_date: 2019,
        price: 100.0,
    };
    println!("title: {}", book.title);
}

结构体初始化

结构体初始化的时候,字段顺序可以随意,但是必须提供所有字段的值。当创建时的时候如果函数的输入参数和结构体成员同名,还可以使用简化的方式创建结构体实例:

fn build_book(title: &str, author: &str, publish_date: i32, price: f32) -> Book {
    Book {
        title: title.to_string(),
        author: author.to_string(),
        publish_date,
        price,
    }
}

fn main() {
    let book = build_book("Rust 入门", "张三", 2019, 100.0);
    println!("title: {}", book.title);
}

还可以通过已有的结构体实例创建新的实例,写法如下:

fn main() {
    let book = Book {
        title: "Rust 入门".to_string(),
        author: "张三".to_string(),
        publish_date: 2019,
        price: 100.0,
    };
    let book1 = Book {
        author: "李四".to_string(),
        ..book
    };
    println!("title: {}", book1.title);
}

..语法表示使用 book 的所有字段,除了 author 字段。如果完全和book 一样,那就删除author: "李四".to_string(),这样就创建了和book内容完全一致的book1结构体实例。

tips:..语法需要在结构体的尾部使用

但是上面的操作有一个缺点,就是title字段的操作会发生所有权转移,因为title字段是可变的,所以所有权会转移到book1结构体实例中,所以book结构体实例的title字段后面就无法访问了。其余字段要么单独声明,要么使用了copy所以还是可以使用。

下面我们看以下结构体的内存排序:还是以上面的例子

fn main() {
    let book = Book {
        title: "Rust 入门".to_string(),
        author: "张三".to_string(),
        publish_date: 2019,
        price: 100.0,
    };
    println!("title: {}", book.title);
}

上面的代码中,结构体实例在内存中会有两个引用分别指向title和author字段,title和author字段会分别存储在两个引用指向的内存中。对于publish_date和price字段,由于它们是不可变的,所以它们会存储在结构体实例的内存中。

结构体字段的可变性:必须要将结构体实例声明为可变的,才能修改其中的字段,Rust 不支持将某个结构体某个字段标记为可变。

单元结构体

结构体实例的内存中也没有字段,结构体实例的内存中只有一个引用,指向结构体实例的内存。当你定义一个类型但是并不关心内容的时候,就可以使用该类型,下面是单元结构体的使用的例子:

struct Unit;

let uint = Unit;

impl someTrait for Unit { /* ... */ }

上面的声明方式中,我们不关心 Unit 的字段数据,只关心它的行为,因此将它声明为单元结构体,然后再为它实现某个特征。

结构体数据的所有权

结构体的字段可以是可变的,也可以是不可变的,但是字段的所有权是不同的。结构体可以从其他对象借用数据,但是需要保证结构体实例的生命周期必须小于它借用对象的生命周期。这块后面学习生命周期的时候详细在讨论。

结构体字段的可变性

结构体字段默认为不可变,使用mut关键字可以使其可变。不支持单独申明某个字段为可变,只能将整个结构体声明为可变。

使用 #[derive(Debug)] 来打印结构体的信息

在前面的例子中,我们直接使用println!来打印信息,但是却不能直接打印我们刚才声明的book结构体,之所以可以打印其他的基础变量是因为基本类型都实现了display特征,但是我们声明的结构体并没有实现display特征,rust的结构体不会默认实现display特征,需要我们手动实现或者使用#[derive(Debug)]来让编译器自动实现。我们看一下使用#[derive(Debug)]实现的代码:

#[derive(Debug)]
struct Book {
    title: String,
    author: String,
    publish_date: i32,
    price: f32,
}

fn main() {
    let book = Book {
        title: "Rust 入门".to_string(),
        author: "张三".to_string(),
        publish_date: 2019,
        price: 100.0,
    };
    println!("{:?}", book);
}

运行结果:

Book { title: "Rust 入门", author: "张三", publish_date: 2019, price: 100.0 }

还有一个简单的输出 debug 信息的方法,那就是使用 dbg! 宏,它会拿走表达式的所有权,然后打印出相应的文件名、行号等 debug 信息,当然还有我们需要的表达式的求值结果。除此之外,它最终还会把表达式值的所有权返回。

dbg! 输出到标准错误输出 stderr,而 println! 输出到标准输出 stdout。

把上面代码中的println!改为dbg!,运行结果如下:

[src\main.rs:16] "{:?}" = "{:?}"
[src\main.rs:16] book = Book {
    author: "张三",
    price: 100.0,
    publish_date: 2019,
    title: "Rust 入门",
}

使用 dbg! 宏,我们可以在调试模式下查看表达式的值,同时还可以看到表达式所在的文件名和行号等信息。方便问题定位。

posted @   NoodlesYang  阅读(31)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示