【Rust 中级教程】 06 trait (4)

简介: 【Rust 中级教程】 06 trait (4)

0x00 开篇


本篇文章将继续向大家介绍下在 Rust 标准库中常用和常见的一些 trait。


0x01 Sized 和 ?Sized


SizedUnSized 这是一种标记 trait (marker trait),他没有方法或者关联类型。Rust 为其适用的所有类型都自动实现了这个 trait,任何人都不能自己实现。当然它也不可以同 derive 一起使用。

Sized 标识固定大小类型,即他们的值在内存中的大小都是相同的。比如每个 f64 类型占8个字节,i32 类型占4个字节等等。


?Sized 表示非固定大小类型,即他们的值大小并不固定,比如前面文章讲到过的字符串切片类型 &str[T] 类型的数组。说白了,非固定大小的类型基本都是占两个字节的胖指针或者是 trait object(有关trait object 的概念暂时不会介绍)。


在我们声明泛型结构体时的时候,通常是 struct<T> 来声明。然而 Rust 则会理解为 struct<T: Sized>。这是因为非固定大小的类型具有非常大的局限性,因此大多数的泛型都被限制使用 Sized 类型。如果你不想限制 T,则需要显示的表示出来。

/// 显式标明
struct MySized<T: ?Sized> {
    value: T,
}

有关这 Sized trait 暂时作为了解即可。


0x02 Default


Default 是 Rust 中提供默认值类型的一个 trait,通常作用于 struct 上。该类型可以同 derive 一起使用,任何人都不能自己。

源码(default.rs):

pub trait Default: Sized {
    fn default() -> Self;
}


源码很简单,就只有一个 default 函数当在结构体上使用 #[derive(Default)] 时,Rust 将会自动为其实现一些基本类型参数的默认值。示例代码如下:

#[derive(Default, Debug)]
struct Test1 {
    a: i32,
    b: f64,
    c: bool,
    d: char,
    e: (),
    f: String,
}
fn main() {
    let test = Test1::default();
    println!("{:#?}", test);
    // 运行结果:
    // Test1 {
    //     a: 0,
    //     b: 0.0,
    //     c: false,
    //     d: '\0',
    //     e: (),
    //     f: "",
    // }
}


其实参数的默认值我们也可以从源码中找到:

default_impl! { (), (), "Returns the default value of `()`" }
default_impl! { bool, false, "Returns the default value of `false`" }
default_impl! { char, '\x00', "Returns the default value of `\\x00`" }
default_impl! { usize, 0, "Returns the default value of `0`" }
default_impl! { u8, 0, "Returns the default value of `0`" }
default_impl! { u16, 0, "Returns the default value of `0`" }
default_impl! { u32, 0, "Returns the default value of `0`" }
default_impl! { u64, 0, "Returns the default value of `0`" }
default_impl! { u128, 0, "Returns the default value of `0`" }
default_impl! { isize, 0, "Returns the default value of `0`" }
default_impl! { i8, 0, "Returns the default value of `0`" }
default_impl! { i16, 0, "Returns the default value of `0`" }
default_impl! { i32, 0, "Returns the default value of `0`" }
default_impl! { i64, 0, "Returns the default value of `0`" }
default_impl! { i128, 0, "Returns the default value of `0`" }
default_impl! { f32, 0.0f32, "Returns the default value of `0.0`" }
default_impl! { f64, 0.0f64, "Returns the default value of `0.0`" }


使用 #[derive(Default)] 标记的结构体,其参数也必须都实现 Default trait。另外,我们也可以自己实现 Default trait。示例代码如下:

#[derive(Debug)]
struct Test2 {
    a: i32,
    b: f64,
    c: bool,
}
/// 默认实现 Default
impl Default for Test2 {
    fn default() -> Self {
        return Self {
            a: 10,
            b: 20.0,
            c: true,
        };
    }
}
fn main() {
    let test2 = Test2::default();
    println!("{:#?}", test2);
    // 运行结果
    // Test2 {
    //     a: 10,
    //     b: 20.0,
    //     c: true,
    // }
}


0x03 编译器做了什么?


看到这里,大家有没有好奇,我们添加 #[derive] 后,编译器到底做了什么呢?其实,我们可以通过 cargo-expand 看到。以Debug 为例。我们将下面的代码通过命令展开。

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let rec = Rectangle {
        width: 3,
        height: 5,
    };
    println!("{:?}", rec);
}


首次使用请先通过以下命令安装。

cargo install cargo-expand


下面的命令必须使用 Rust nightly 版本

cargo rustc --profile=check -- -Zunpretty=expanded


展开后的代码如下:

#![feature(prelude_import)]                                                                                                                                                             
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
struct Rectangle {
    width: u32,
    height: u32,
}
#[automatically_derived]
impl ::core::fmt::Debug for Rectangle {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Rectangle",
            "width", &&self.width, "height", &&self.height)
    }
}
fn main() {
    let rec = Rectangle { width: 3, height: 5 };
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(&["", "\n"],
                &[::core::fmt::ArgumentV1::new_debug(&rec)]));
    };
}


很清楚的可以看到,编译器为 Rectangle 结构体实现了 Debug trait。


0x04 小结


有关常用的一些 trait 暂时先介绍到这里,当然其实还有一些 trait 我还没有写出来, 比如:Copy,Clone 等。其实介绍这些 trait 还需要一些前置知识的了解。

在 Rust 中,trait 类型是最重要的类型,它是 Rust 中唯一的接口抽象方式,可以很方便的让开发者在多种类型上定义统一的行为,这对于项目开发尤为重要。有关 trait 的文章目前已经有4篇了,我想可以暂告一个段落了。这里还留了一个小尾巴—— trait object,我也将会放到后面去介绍。

相关文章
|
3月前
|
存储 Rust
30天拿下Rust之Trait
30天拿下Rust之Trait
60 0
|
5月前
|
Rust
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
|
Rust 安全 JavaScript
Rust教程初识
当然,它们都有编译然后执行可执行程序的用法。应当有编译后改名的方式,不然那也太不coooooooooooool了。
90 0
|
存储 消息中间件 Rust
Rust极简教程
Rust是一门赋予每个人构建可靠且高效软件能力的编程语言。可靠主要体现在安全性上。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软件。
332 0
Rust极简教程
|
Rust 安全 Java
Rust学习笔记之泛型、trait 与生命周期
1. 泛型大补汤 推荐阅读指数 ⭐️⭐️⭐️⭐️ 2. 泛型数据类型 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 3. trait:定义共享的行为 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 4. 生命周期与引用有效性 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
115 0
|
Rust 编译器
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)
|
Rust 编译器
Rust 中级教程 第16课——引用的 lifetime(1)
Rust 中级教程 第16课——引用的 lifetime(1)
Rust 中级教程 第16课——引用的 lifetime(1)
|
Rust C++
【Rust 中级教程】 15 引用与借用(3)
【Rust 中级教程】 15 引用与借用(3)
【Rust 中级教程】 15 引用与借用(3)
|
Rust 编译器
【Rust 中级教程】 13 引用与借用(1)
【Rust 中级教程】 13 引用与借用(1)
【Rust 中级教程】 13 引用与借用(1)
|
Rust 安全 编译器
【Rust 中级教程】 12 共享所有权
【Rust 中级教程】 12 共享所有权
【Rust 中级教程】 12 共享所有权