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》

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

相关推荐
zb200641207 分钟前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
青柠代码录18 分钟前
【MySQL】事务:事务的隔离级别
后端
分享牛22 分钟前
Operaton入门到精通22-Operaton 2.0 升级指南:Spring Boot 4 核心变更详解
java·spring boot·后端
jinanmichael22 分钟前
SpringBoot 如何调用 WebService 接口
java·spring boot·后端
深蓝轨迹23 分钟前
吃透 Spring Boot dataSource与Starter
java·spring boot·笔记·后端
spring29979225 分钟前
springboot和springframework版本依赖关系
java·spring boot·后端
yuhaiqiang29 分钟前
为什么这道初中数学题击溃了所有 AI
前端·后端·面试
面向Google编程1 小时前
从零学习Kafka:副本机制
大数据·后端·kafka
超级大福宝1 小时前
用买火车票的例子讲解Java反射的作用
java·开发语言·后端
程序员爱钓鱼1 小时前
Go高性能缓冲IO详解: bufio包深度指南
后端·面试·go