小工具知识网

当前位置: 首页 > 行业知识 > 【Rust】——结构体struct

行业知识

【Rust】——结构体struct

2026-11-02 01:11:14 来源:互联网转载

个人专���:

算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客

Java基础:Java基础_IT闫的博客-CSDN博客

c语言:c语言_IT闫的博客-CSDN博客

MySQL:数据结构_IT闫的博客-CSDN博客

数据结构:​​​​​​数据结构_IT闫的博客-CSDN博客

C++:C++_IT闫的博客-CSDN博客

C51单片机:C51单片机(STC89C516)_IT闫的博客-CSDN博客

基于HTML5的网页设计及应用:基于HTML5的网页设计及应用_IT闫的博客-CSDN博客​​​​​​

python:python_IT闫的博客-CSDN博客

离散数学:离散数学_IT闫的博客-CSDN博客

​​​​​​Linux:​​​​Linux_Y小夜的博客-CSDN博客

Rust:Rust_Y小夜的博客-CSDN博客

欢迎收看,希望对大家有用!

目录

定义并实例化struct

什么是struct

定义struct

实例化struct

struct例子

struct

struct方法

方法调用的运算符

方法参数

关联函数

多个impl块


定义并实例化struct

什么是struct

  • struct,结构体

    -自定义的数据类型

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

    定义struct

            定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 字段(field)。

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

            定义完每个字段的名称后,要用逗号分割,包括最后一个。

    实例化struct

        一旦定义了结构体后,为了使用它,通过为每个字段指定具体值来创建这个结构体的 实例。创建一个实例需要以结构体的名字开头,接着在大括号中使用 key: value 键 - 值对的形式提供字段,其中 key 是字段的名字,value 是需要存储在字段中的数据值。实例中字段的顺序不需要和它们在结构体中声明的顺序一致。换句话说,结构体的定义就像一个类型的通用模板,而实例则会在这个模板中放入特定数据来创建这个类型的值。

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

    为了从结构体中获取某个特定的值,可以使用点号。

    • 注意整个实例必须是可变的;
    • Rust 并不允许只将某个字段标记为可变。
    • 另外需要注意同其他任何表达式一样,我们可以在函数体的最后一个表达式中构造一个结构体的新实例,来隐式地返回这个实例。

      使用字段初始化简写语法:

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

      更新语法:

      fn main() {    // --snip--    let user2 = User {        email: String::from("another@example.com"),        ..user1    };}

      Tuple struct:

              元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。

      struct Color(i32, i32, i32);struct Point(i32, i32, i32);fn main() {    let black = Color(0, 0, 0);    let origin = Point(0, 0, 0);}

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

      struct AlwaysEqual;fn main() {    let subject = AlwaysEqual;}

              为了定义 AlwaysEqual,我们使用 struct 关键字,接着是我们想要的名称,然后是一个分号。不需要花括号或圆括号!

      struct数据所有权:

              我们使用了自身拥有所有权的 String 类型而不是 &str 字符串 slice 类型。这是一个有意而为之的选择,因为我们想要这个结构体拥有它所有的数据,为此只要整个结构体是有效的话其数据也是有效的。

      struct例子

      需求:计算长方形面积

      fn main() {    let w=30;    let l=50;    println!("{}",area(w, l));}fn area(width:u32,length:u32) -> u32{    width*length}

              

              这个示例代码在调用 area 函数时传入每个维度,虽然可以正确计算出长方形的面积,但我们仍然可以修改这段代码来使它的意义更加明确,并且增加可读性。

      使用元组重构:

      fn main() {    let rect=(30,50);    println!("{}",area(rect));}fn area(dim:(u32,u32)) -> u32 {    dim.0*dim.1}

              在某种程度上说,这个程序更好一点了。元组帮助我们增加了一些结构性,并且现在只需传一个参数。不过在另一方面,这个版本却有一点不明确了:元组并没有给出元素的名称,所以计算变得更费解了,因为不得不使用索引来获取元组的每一部分:

              在计算面积时将宽和高弄混倒无关紧要,不过当在屏幕上绘制长方形时就有问题了!我们必须牢记 width 的元组索引是 0,height 的元组索引是 1。如果其他人要使用这些代码,他们必须要搞清楚这一点,并也要牢记于心。很容易忘记或者混淆这些值而造成错误,因为我们没有在代码中传达数据的意图。

      用结构体重构:

        area 函数访问 Rectangle 实例的 width 和 height 字段(注意,访问对结构体的引用的字段不会移动字段的所有权,这就是为什么你经常看到对结构体的引用)。area 的函数签名现在明确的阐述了我们的意图:使用 Rectangle 的 width 和 height 字段,计算 Rectangle 的面积。这表明宽高是相互联系的,并为这些值提供了描述性的名称而不是使用元组的索引值 0 和 1 。结构体胜在更清晰明了。


      像前面章节那样尝试使用 println!,但这并不行。

      struct Rectangle {    width: u32,    height: u32,}fn main() {    let rect1 = Rectangle {        width: 30,        height: 50,    };    println!("rect1 is {}", rect1);}

        {} 默认告诉 println! 使用被称为 Display 的格式:意在提供给直接终端用户查看的输出。目前为止见过的基本类型都默认实现了 Display,因为它就是向用户展示 1 或其他任何基本类型的唯一方式。不过对于结构体,println! 应该用来输出的格式是不明确的,因为这有更多显示的可能性:是否需要逗号?需要打印出大括号吗?所有字段都应该显示吗?由于这种不确定性,Rust 不会尝试猜测我们的意图,所以结构体并没有提供一个 Display 实现来使用 println! 与 {} 占位符。

              错误信息:

         = help: the trait `std::fmt::Display` is not implemented for `Rectangle`   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

              现在 println! 宏调用看起来像 println!("rect1 is {:?}", rect1); 这样。在 {} 中加入 :? 指示符告诉 println! 我们想要使用叫做 Debug 的输出格式。Debug 是一个 trait,它允许我们以一种对开发者有帮助的方式打印结构体,以便当我们调试代码时能看到它的值。

      这样调整后再次运行程序。见鬼了!仍然能看到一个错误:

      error[E0277]: `Rectangle` doesn't implement `Debug`

              Rust 确实 包含了打印出调试信息的功能,不过我们必须为结构体显式选择这个功能。为此,在结构体定义之前加上外部属性 #[derive(Debug)];

      #[derive(Debug)]struct Rectangle {    width: u32,    height: u32,}fn main() {    let rect1 = Rectangle {        width: 30,        height: 50,    };    println!("rect1 is {:?}", rect1);}

              好极了!这并不是最漂亮的输出,不过它显示这个实例的所有字段,毫无疑问这对调试有帮助。当我们有一个更大的结构体时,能有更易读一点的输出就好了,为此可以使用 {:#?} 替换 println! 字符串中的 {:?}。在这个例子中使用 {:#?} 

      struct

      struct方法

      方法(method)与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。

      方法与函数不同之处:

      • 因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文。
      • Rust 让你在第一个参数位置上只用 self 这个名字来缩写。
        #[derive (Debug)]struct  R{    with:u32,    length:u32,}impl R {    fn area(&self) -> u32 {    self.with*self.length}}fn main() {    let rect=R{        with:30,        length:50,    };    println!("{}",rect.area());    println!("{:#?}",rect);}

                使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 self 的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 impl 块中,而不是让将来的用户在我们的库中到处寻找 Rectangle 的功能。

        方法调用的运算符

                Rust 有一个叫 自动引用和解引用(automatic referencing and dereferencing)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。

                它是这样工作的:当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 * 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:

        p1.distance(&p2);(&p1).distance(&p2);

        方法参数

        #[derive (Debug)]struct  R{    with:u32,    length:u32,}impl R {    fn area(&self) -> u32 {    self.with*self.length}    fn b(&self,other:&R)->bool{        self.with>other.with&&self.length>other.length    }}fn main() {    let rect1=R{        with:30,        length:50,    };    let rect2=R{        with:10,        length:40,    };    let rect3=R{        with:35,        length:55,    };    println!("{}",rect1.b(&rect2));    println!("{}",rect1.b(&rect3));}

                因为我们只需要读取 rect2(而不是写入,这意味着我们需要一个不可变借用)

        关联函数

                所有在 impl 块中定义的函数被称为 关联函数(associated functions),因为它们与 impl 后面命名的类型相关。我们可以定义不以 self 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。我们已经使用了一个这样的函数:在 String 类型上定义的 String::from 函数。

        #[derive (Debug)]struct  R{    with:u32,    length:u32,}impl R {    fn area(&self) -> u32 {    self.with*self.length}    fn b(&self,other:&R)->bool{        self.with>other.with&&self.length>other.length    }    fn sq(size:u32) ->R{        R{            with:size,            length:size,        }    }}fn main() {    let s=R::sq(20);    let rect1=R{        with:30,        length:50,    };    let rect2=R{        with:10,        length:40,    };    let rect3=R{        with:35,        length:55,    };    println!("{}",rect1.b(&rect2));    println!("{}",rect1.b(&rect3));}
        • 使用结构体名和 :: 语法来调用这个关联函数
        • 这个函数位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

          多个impl块

          每个结构体都允许拥有多个 impl 块。

          如:

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

          意义不大,但不存在错误。

上一篇:老电脑怎么搭建服务器?

下一篇:hownice的音标怎么写?