rust - 理解 ToOwned trait

2023-10-29 02:45
文章标签 rust 理解 trait toowned

本文主要是介绍rust - 理解 ToOwned trait,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

ToOwned trait支持任意类型的转换,而Clone trait只支持&T 到 T 的转换.以下先介绍一下基本的定义,最后通过一个简单的例子详细理解一下Borrow traitToOwned trait的互相转换的过程.

定义

可以将任意类型T转换为U类型,其中U类型实现了Borrow<T> trait,

  • T: 指的是Self
  • U: 指的是Borrow<Self>

可以简单理解为ToOwned traitBorrow trait反向操作.

pub trait ToOwned {type Owned: Borrow<Self>;fn to_owned(&self) -> Self::Owned;
}

str类型

str已经默认支持了ToOwned trait,如下

impl ToOwned for str {type Owned = String;fn to_owned(&self) -> String {unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) }}

可以将str类型转换为String类型,String需要实现 Borrow<str> trait,如下

impl Borrow<str> for String {#[inline]fn borrow(&self) -> &str {&self[..]}
}

下面举一个简单的例子:

#[test]
fn test_string_borrow() {let s = "hello";let t: String = s.to_owned();assert_eq!(t, s.to_string());let s = "world";let t: String = s.to_owned();assert_eq!(t, s.to_string());
}

使用场景

一个班级有多名学生,每个学生有一个唯一的学号,根据学号可以唯一确认这名学生.可以使用 trait 来描述学生和学号之间的关系.

  • 学生类使用Borrow trait 可以实现获取学生的唯一学号.
  • 学号类使用ToOwned trait可以实现根据学号获取学生实例.

通过使用Borrow traitToOwned trait,实现了学生对象和学号对象之间的互转.

下面来看下如何实现这个例子

1. 班级类

使用 HashMap 记录了所有的学生信息.其中 key 表示学号,后续可以通过学号获取学生对象.

#[derive(Debug)]
struct SchoolClass {students: HashMap<String, Rc<Student>>,name: String,
}

2. 学生类

包含了学号类、学生的基本属性、还有所在的班级.如果从数据库约束的角度考虑,可以理解Student中包含了名为班级的外键class.

/// 学生类
#[derive(Debug)]
struct Student {no: StudentNo,                   // 学生编号对象name: String,                    // 学生名称age: u8,                         // 学生年纪class: Rc<RefCell<SchoolClass>>, // 学生所在的班级对象
}

使用Borrow traitStudent类型转换为&StudentNo学号类,如下

impl Borrow<StudentNo> for Student {fn borrow(&self) -> &StudentNo {&self.no}
}

3. 学号类

包含一个唯一的编号值,还需要说明学号属于哪个班级,用于后续从班级中根据学号查询学生.

/// 学生编号类
#[derive(Debug)]
struct StudentNo {no: String,                      // 学生编号值class: Rc<RefCell<SchoolClass>>, // 学生所在的班级
}

使用ToOwned traitStudentNo类型转换为Student类型,如下

/// 根据学生编号值获得对应的学生
impl ToOwned for StudentNo {type Owned = Student;fn to_owned(&self) -> Self::Owned {// 在班级中根据学生编号值查询学生let class = self.class.try_borrow().unwrap();let student = class.fetch_student(&self.no.to_string()).unwrap();// 生成新的学生对象Student {no: StudentNo {no: self.no.clone(),class: Rc::clone(&self.class),},name: student.name.clone(),age: student.age,class: Rc::clone(&self.class),}}
}

4.完整的例子

use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;#[test]
fn test_to_owned() {/// 学生编号类#[derive(Debug)]struct StudentNo {no: String,                      // 学生编号值class: Rc<RefCell<SchoolClass>>, // 学生所在的班级}/// 根据学生编号值获得对应的学生impl ToOwned for StudentNo {type Owned = Student;fn to_owned(&self) -> Self::Owned {// 在班级中根据学生编号值查询学生let class = self.class.try_borrow().unwrap();let student = class.fetch_student(&self.no.to_string()).unwrap();// 生成新的学生对象Student {no: StudentNo {no: self.no.clone(),class: Rc::clone(&self.class),},name: student.name.clone(),age: student.age,class: Rc::clone(&self.class),}}}/// 学生类#[derive(Debug)]struct Student {no: StudentNo,                   // 学生编号对象name: String,                    // 学生名称age: u8,                         // 学生年纪class: Rc<RefCell<SchoolClass>>, // 学生所在的班级对象}impl fmt::Display for Student {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {fmt.pad(self.name.as_str())}}impl Borrow<StudentNo> for Student {fn borrow(&self) -> &StudentNo {&self.no}}#[derive(Debug)]struct SchoolClass {students: HashMap<String, Rc<Student>>,name: String,}/// 班级类impl SchoolClass {fn new(name: String) -> Rc<RefCell<SchoolClass>> {Rc::new(RefCell::new(SchoolClass {name: name,students: HashMap::new(),}))}/// 添加学生到班级fn add_student(&mut self, no: String, student: Rc<Student>) {self.students.insert(no, student);}/// 根据学生名称获得学生对象fn fetch_student(&self, no: &String) -> Option<&Rc<Student>> {self.students.get(no)}}impl fmt::Display for SchoolClass {fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {fmt.pad(self.name.as_str())}}// 创建一个班级对象let class_name = "First class";let class = SchoolClass::new(class_name.to_string());// 创建一个学生对象let student_name = "bob";let no = "A001";let student = Student {no: StudentNo { no: no.to_string(), class: Rc::clone(&class) },name: student_name.to_string(),age: 18,class: Rc::clone(&class),};// 添加学生到班级中{class.borrow_mut().add_student(no.to_string(), Rc::new(student));}// 根据学生名称查询学生// Note: 在使用了 std::borrow::Borrow的情况下,注意不能用 class.borrow(), 因为与 RefCell 的 borrow()冲突,所以使用try_borrow()替代let class_a = class.try_borrow().unwrap();let student_bob = class_a.fetch_student(&no.to_string()).unwrap();assert_eq!(student_bob.name, student_name.to_string());// 使用 Borrow trait 获得学生的学号let student = student_bob.as_ref();let student_no: &StudentNo = student.borrow(); // 必须显示标注类型,否则会与默认的 Borrow Trait 冲突assert_eq!(student_no.no, no.to_string());// 使用 ToOwned trait 根据学号获得学生实例let student_bob = student_no.to_owned();assert_eq!(student_bob.name, student_name.to_string());
}

这篇关于rust - 理解 ToOwned trait的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/297742

相关文章

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Rust中的Drop特性之解读自动化资源清理的魔法

《Rust中的Drop特性之解读自动化资源清理的魔法》Rust通过Drop特性实现了自动清理机制,确保资源在对象超出作用域时自动释放,避免了手动管理资源时可能出现的内存泄漏或双重释放问题,智能指针如B... 目录自动清理机制:Rust 的析构函数提前释放资源:std::mem::drop android的妙

Rust中的BoxT之堆上的数据与递归类型详解

《Rust中的BoxT之堆上的数据与递归类型详解》本文介绍了Rust中的BoxT类型,包括其在堆与栈之间的内存分配,性能优势,以及如何利用BoxT来实现递归类型和处理大小未知类型,通过BoxT,Rus... 目录1. Box<T> 的基础知识1.1 堆与栈的分工1.2 性能优势2.1 递归类型的问题2.2

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.