Rust的结构体(struct)是一种自定义的数据类型,允许开发者命名和包装多个相关的值,以形成有意义的数据组合。在Rust中结构体不仅用于数据组织,还密切结合了Rust的内存安全性和所有权模型特性,在开发系统编程过程中很有用。

定义和实例化

使用struct关键字来定义一个结构体。可以定义字段的类型,但所有字段都必须在创建实例时进行指定。

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
//创建结构体  
//初始化实例时,每个字段都需要进行初始化
//初始化时的字段顺序不需要和结构体定义时的顺序一致
let user1 = User {
    email: String::from("example@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

//通过.操作符即可访问结构体实例内部的字段值 
user1.email = String::from("anotheremail@example.com");  

//通过旧的结构体内容初始化新的结构体
let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };
//缺省的初始化复制     
//除了email之外 其它变量都用user1的 username字段发生了所有权转移  作为结果 user1无法再被使用  
//把结构体中具有所有权的字段转移出去后,将无法再访问该字段,但是可以正常访问其它的字段   
let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    }; 

可变性

在Rust中结构体实例的可变性由整个实例来控制。如果一个实例被声明为可变的,那么所有的字段都是可变的。

//声明可变的结构体  
let mut user1 = User {
    // 初始化代码相同
};
user1.email = String::from("another@example.com");

构造函数

Rust没有专门的构造函数语法,但可以通过实现关联函数(通常是new)来模拟构造函数行为。

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
impl User {
    fn new(email: String, username: String) -> User {
        User {
            email,
            username,
            active: true,
            sign_in_count: 1,
        }
    }
}

let user2 = User::new(String::from("test@test.com"), String::from("testuser"));

方法定义

结构体可以包含方法,方法是定义在impl块中的函数,它们可以访问结构体的字段和其他方法。 Rust中的函数分为两类:
1.实例方法(有self参数) 直接作用于结构体的实例,可以访问和修改实例的属性。
2.关联函数(无self参数) 与结构体类型关联,但不作用于具体的实例。它们常用于执行不需要直接访问结构体字段的操作,如构造新实例。

当方法包含self参数时,它们是实例方法。这意味着它们操作的是结构体的一个具体实例,可以访问和修改实例的数据。
self 参数可以以三种形式出现:

  • self: 这种方式获取结构体的所有权,通常用于需要消耗结构体实例的场景。
  • &self: 这是最常见的形式,代表对结构体实例的不可变引用,用于当方法只需要读取而不修改结构体数据时。
  • &mut self: 代表对结构体实例的可变引用,用于需要修改实例数据的方法。

不包含self参数的方法被称为关联函数。这些函数与结构体类型相关联,但不与结构体的某个具体实例相关联。它们类似于其他语言中的静态方法。关联函数通常用于构造器(创建结构体实例的函数)或与结构体逻辑相关但不依赖于具体实例的工具函数。

impl User {
    //实例方法  
    fn email(&self) -> &str {
        &self.email
    }
    //关联函数  
    fn new(email: String, username: String) -> User {
        User {
            email,
            username,
            active: true,
            sign_in_count: 1,
        }
    }
}

println!("User email: {}", user1.email());

特殊结构体

在Rust中,除了常规的命名字段结构体外,还有两种特殊类型的结构体:元组结构体(tuple structs)和单元结构体(unit structs)。这两种结构体提供了更多的灵活性和表达力,以适应不同的编程需求。

元组结构体本质上是命名元组。它们的字段没有名字,只有类型,适用于需要打包几个数据但不需要为每个数据字段命名的场景。元组结构体的语法和元组类似,但是它有一个具体的类型名称。当你需要创建一个结构体,但字段名不重要或者可以省略时,元组结构体是一个不错的选择。它们简化了代码,尤其是在字段名可能会增加语义重复的情况下。

//元组结构体  
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);  

单元结构体没有任何字段,通常用于表示不需要存储数据的类型,但你想利用它的类型安全优势。它们类似于空元组()(也称为单元类型)。单元结构体的一个用途是实现特定的trait而不存储数据。例如,你可能有一个行为像标记的trait,这些trait通过单元结构体来实现,以便能够用类型系统强制某种特定的行为或属性。常用于标记类型,或在泛型编程中作为类型占位符,还可以在实现无状态的trait时使用。

//单元结构体 没有任何属性  
struct AlwaysEqual;
let subject = AlwaysEqual;
//不关心AlwaysEqual的字段数据,只关心它的行为   
impl SomeTrait for AlwaysEqual {
}

输出结构体

要在Rust中输出结构体的内容,你需要为该结构体实现Debug或Display特性。Debug 特性主要用于调试目的,它会输出结构体的所有字段,而Display特性用于更正式的输出,可以自定义输出内容。

实现Debug特性

#[derive(Debug)]
struct Person {
    name: String,
    age: u8
}
let person = Person {
    name: String::from("Alice"),
    age: 30
};
// 使用 {:?} 来格式化输出 Debug 版本
println!("{:?}", person); 

实现Display特性

use std::fmt;

struct Person {
    name: String,
    age: u8
}

impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} is {} years old", self.name, self.age)
    }
}

let person = Person {
    name: String::from("Alice"),
    age: 30
};

println!("{}", person); // 使用 {} 来格式化输出 Display 版本

与C++结构体的差异

1.内存安全性
Rust结构体在编译时强制执行所有权和借用规则,从而无需担心空悬指针和内存泄漏。C++则依赖于程序员对指针和内存管理的手动控制。

2.方法和成员函数
C++允许类成员默认可变。如果你需要不可变性,你必须明确地使用const关键词。而在Rust中,不可变性是默认的,可变性必须明确声明。

3.构造函数和析构函数
C++有构造函数和析构函数的概念,用于初始化和清理资源。Rust则使用所有权系统自动处理资源清理,不需要析构函数,虽然可以实现Drop trait来定制清理行为。

4.继承
C++支持类的继承。而Rust不支持传统的面向对象编程中的继承,而是使用特性(traits)和组合来达到类似的功能复用。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部