Rust结构体初探

结构体用来组合不同类型的数据,类似于 C 语言当中的结构体。定义结构体时,需要声明变量名以及变量的类型。

结构体定义与返回结构体

使用花括号,然后指明每一个字段的值来初始化结构体变量。

rust 复制代码
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    let user: User = User {
        username: String::new("Jack"),
        email: String::new("1649412@qq.com"),
        active: true,
        sign_in_count: 125554,
    };
}

为了访问结构体中的某个成员变量,可以使用 . 操作符:
println("username is {user.username}");

注意整个结构体的每个字段都必须是可变的,Rust 不允许只将结构体的某个字段标记为可变。

另外,当函数的返回类型是一个结构体类型时,我们可以在函数的最后一个表达式中构造一个新的结构体变量。下面是一些返回结构体类型的例子。

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: username,
        email: email,
        sign_in_count: 1,
    }
}

如果 build_user 函数的参数名与结构体的某个字段名相同,可以使用字段初始化简写语法,这样就可以不用显示指出这个字段的值了:

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: ,
        email: ,
        sign_in_count: 1,
    }
}

有时候,一个结构体中许多字段的值是来自于同类型另一个结构体变量的字段,这个时候可以使用结构体更新语法(struct update syntax)来简化结构体变量的初始化过程:

rust 复制代码
fn main() {
    // --snip--

    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };
    // 可以简化为
    let user3 = User {
        email: String::from("another@example.com"),
        ..user1
    };
}

但是,..user1 必须放到最后。此外,在使用一个结构体变量的成员初始化另一个结构体变量的成员时,对于没有实现 copy trait 的数据类型,会发生所有权的转移,例如上例当中的 username

特殊形式的结构体

元组结构体(tuple structs)

元组结构体没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组的类型区别于其它元组类型时,元组结构体会比较有用。

rust 复制代码
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
    println!("Point = {origin.0, origin.1, origin.2}");
}

ColorPoint 是两种不同的类型,尽管它们每个字段的类型都相同。

没有任何字段的类单元结构体

没有任何字段的结构体称为类单元结构体(unit-like structs)。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。

rust 复制代码
struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

定义类单元结构体不需要 {},定义一个类结构体变量也不需要 {}

结构体的方法和关联函数

方法

方法和函数的不同之处在于:方法在结构体、枚举类型或者 trait 的上下文中定义,并且它们的第一个参数总是 self,它代表调用该方法的结构体实例。

方法第一个参数的类型:

  • &self 实际上是 self:&Self 的缩写;在一个 impl 块当中,Selfimpl 块的类型的别名。
  • &mut self:如果想要改变实例当中的成员,参数的类型就需要写成 &mut self
  • self:通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
rust 复制代码
// 用于获得debug trait
#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}
// 方法的实现。可以分段写。
impl Rectangle {
  fn area(&self) -> u32 {
      self.width * self.height
  }
  fn perimeter(&self) -> u32 {
      2 * (self.height + self.width)
  }

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

  // 关联函数。属于整个结构体,不需要创建出实例就能使用
  fn square(size: u32) -> Rectangle {
      Rectangle {width:size, height:size}
  }
}

fn main() {
  let rect1 = Rectangle { width: 30, height: 50 };
  let rect2 = Rectangle { width: 10, height: 40 };
  let rect3 = Rectangle { width: 60, height: 45 };
  let square1: Rectangle = Rectangle::square(32);

  println!("rect1 is {:#?}", rect1); // 格式化输出debug
  println!("square1 is {:?}", square1);
  println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
  println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
  • {:?} 用于格式化输出实现了 Debug 特征的结构体;例如:
    square1 is Rectangle { width: 32, height: 32 }
  • {:#?} 以一种更易读的方式输出实现了Debug 特征的结构体;
rust 复制代码
rect1 is Rectangle {
   width: 30,
   height: 50,
}

关联函数

所有不在 impl 块中定义的函数被称为关联函数(associated functions)。关联函数第一个参数的类型不必是 self,因为它并不作用于一个结构体的实例。关联函数通常用来创建并返回一个结构体新实例,类似于其它编程语言当中的构造函数。通常这个函数命名为 new,但不是必须的,因为 new 并不是 Rust 语言的关键字。例如,我们不使用 new 也可以用来创建对象:

rust 复制代码
impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}
fn main()
{
    let s = Rectangle::squre(20);
}

小结:本篇博客介绍了 Rust 语言当中结构体的定义,初始化结构的三种方式,两种特殊的结构体,以及方法和关联函数的定义。

文章主要参考资料:《The Rust Programming Language》

各位道友,码字不易,如有收获,记得一键三连啊。

相关推荐
王码码20354 小时前
Go语言的测试:从单元测试到集成测试
后端·golang·go·接口
王码码20354 小时前
Go语言中的测试:从单元测试到集成测试
后端·golang·go·接口
嵌入式×边缘AI:打怪升级日志4 小时前
使用JsonRPC实现前后台
前端·后端
小码哥_常5 小时前
从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代
后端
lolo大魔王6 小时前
Go语言的异常处理
开发语言·后端·golang
IT_陈寒8 小时前
Python多进程共享变量那个坑,我差点没爬出来
前端·人工智能·后端
码事漫谈8 小时前
2026软考高级·系统架构设计师备考指南
后端
AI茶水间管理员9 小时前
如何让LLM稳定输出 JSON 格式结果?
前端·人工智能·后端
其实是白羊9 小时前
我用 Vibe Coding 搓了一个 IDEA 插件,复制URI 再也不用手动拼了
后端·intellij idea
用户8356290780519 小时前
Python 操作 Word 文档节与页面设置
后端·python