玩转Rust高级应用. ToOwned trait 提供的是一种更“泛化”的Clone 的功能,Clone一般是从&T类型变量创造一个新的T类型变量

标准库简介

除了前面介绍过的容器、迭代器之外,标准库还提供了一系列有用的类型、函数、 trait 等。本章挑选其中比较常见的一部分简单介绍。

类型转换

Rust 给我们提供了一个关键字as 用于基本类型的转换。但是除了基本类型之外,还有 更多的自定义类型,它们之间也经常需要做类型转换。为此, Rust 标准库给我们提供了一系 列 的trait 来辅助抽象。

AsRef/AsMut

AsRef 这个trait 代表的意思是,这个类型可以通过调用as_ref方法,得到另外一个 类型的共享引用。它的定义如下:

js 复制代码
pub trait AsRef < T: ?Sized > {
    fn as_ref( & self) - >&T;
}

同 理 ,ASMut 有 一 个 as_mut方法,可以得到另外一个类型的可读写引用:

js 复制代码
pub trait ASMut < T: ?Sized > {
    fn as_mut( & mut self) - >&mut T;
}

比如说,标准库里面的String类型,就针对好几个类型参数实现了AsRef trait:

js 复制代码
impl AsRef < str >
for String impl AsRef < [u8] >
for String impl AsRef < 0sStr >
for String impl AsRef < Path >
for String

AsRef 这样的trait 很适合用在泛型代码中,为一系列类型做统一抽象。比如,我们可 以写一个泛型函数,它接受各种类型,只要可以被转换为&[u8] 即可:

js 复制代码
fn iter_bytes < T: AsRef < [u8] >> (arg: T) {
    for i in arg.as_ref() {
        println ! ("{}", i);
    }
}
fn main() {
    let s: String = String: :from("this is a string");
    let v: Vec < u8 >= vec ! [1, 2, 3];
    let c: &str = "hello";
}

相当于函数重载。只不过基于泛型实现的重载,一定需要重载的参数类型满足某种共同的约束 iter_bytes(s);

js 复制代码
{
  iter_bytes(V);
  iter_bytes(c);
}

Borrow/BorrowMut

Borrow 这个trait 设计得与AsRef 非常像。它是这样定义的:

js 复制代码
pub trait Borrow < Borrowed: ?Sized > {
    fn borrow( & self) - >&Borrowed;
}

可以说,除了名字之外,它和AsRef 长得一模一样。但它们的设计意图不同。比如, 针对String 类型,它只实现了一个Borrow:

js 复制代码
impl Borrow<str>for String

这是因为这个trait 一般被用于实现某些重要的数据结构,比如HashMap:

js 复制代码
impl HashMap {
    pub fn get < Q: ?Sized > ( & self, k: &Q) - >Option < &V > where K: Borrow < Q > ,
    Q: Hash + Eq {}
}

和 BTreeMap:

js 复制代码
impl BTreeMap {
    pub fn get < Q: ?Sized > ( & self, key: &Q) - >Option < &V > where K: Borrow < Q > ,
    Q: Ord {}
}

所以,它要求borrow() 方法返回的类型,必须和原来的类型具备同样的hash值,以及排序。这是一个约定,如果实现Borrow trait的时候违反了这个约定,那么把这个类型放 到 HashMap 或者BTreeMap 里面的时候就可能出现问题。


From/Into

AsRef/Borrow做的类型转换都是从一种引用&T 到另一种引用&U的转换。而From/Into 做的则是从任意类型T 到 U 的类型转换:

js 复制代码
pub trait From < T > {
    fn from(T) - >Self;
}
pub trait Into < T > {
    fn into(self) - >T;
}

显然,From 和 Into 是互逆的一组转换。如果T 实现了From++, 那 么U理应实现 Into 。 因此,标准库里面提供了这样一个实现:++

js 复制代码
impl < T,
U > Into < U >
for T where U: From < T > {
    fn into(self) - >U {
        U: :from(self)
    }
}

用自然语言描述,意思就是:如果存在U:From, 则实现T:Into++。++

正是因为标准库中已经有了这样一个默认实现,我们在需要给两个类型实现类型转换的 trait的时候,写一个From 就够了,Into 不需要自己手写。

比如,标准库里面已经给我们提供了这样的转换:

js 复制代码
impl<a>From<&a str>for String

这意味着&str 类型可以转换为String 类型。我们有两种调用方式: 一种是通过 String::from(&str) 来使用,一种是通过&str::into() 来使用。它们的意思一样:

js 复制代码
fn main() {
    let s: &'static str ="hello";
    let str1:String =s.into();
    let str2:String =String::from(s);
}

另外,由于这几个trait很常用,因此Rust已经将它们加入到prelude中。在使用的时 候我们不需要写use std::convert::From;这样的语句了,包括AsRef、ASMut、Into、From、ToOwned 等。具体可以参见libstd/prelude/v1.rs源代码的内容。

标准库中还有一组对应的TryFrom/TryInto 两个trait, 它们是为了处理那种类型转 换过程中可能发生转换错误的情况。因此,它们的方法的返回类型是Result类型。


ToOwned

ToOwned trait 提供的是一种更"泛化"的Clone 的功能。Clone 一般是从&T 类型变 量创造一个新的T 类型变量,而ToOwned 一般是从一个&T 类型变量创造一个新的U类型 变 量 。

在标准库中,ToOwned 有一个默认实现,即调用clone 方法:

js 复制代码
impl < T > ToOwned
for T where T: Clone {
    type Owned = T;
    fn to_owned( & self) - >T {
        self.clone()

        fn clone_into( & self, target: &mut T) {
            target.clone_from(self);
        }
    }
}

但是,它还对一些特殊类型实现了这个trait 。比如:

js 复制代码
impl < T: Clone > ToOwned
for [T] {
    type Owned = Vec < T > ;
}
impl ToOwned
for str {
    type Owned = String;
}

而且,很有用的类型Cow 也是基于ToOwned 实现的:

js 复制代码
pub enum Cow < 'a,B>
where
B:'a + ToOwned + ?Sized,
{
    Borrowed( & 'a B),
    Owned(<B as ToOwned>::Owned),
}
// ToString trait提供了其他类型转换为String 类型的能力。
pub trait ToString{
   fn to_string(&self)->String;
}

一般情况下,我们不需要自己为自定义类型实现ToString trait。因为标准库中已经提供 了 一 个 默 认 实 现 :

js 复制代码
impl < T: fmt: :Display + ?Sized > ToString
for T {# [inline]
default fn to_string( & self) - >String {
        use core: :fmt: :Write;
        let mut buf = String: :new();
        buf.write_fmt(format_args ! ("{}", self)).expect("a Display implementation return an error     unexpectedly");
        buf.shrink_to_fit();
        buf
    }
}

这意味着,任何一个实现了Display trait 的类型,都自动实现了ToString trait。而 Display trait 是可以自动derive 的,我们只需要为类型添加一个attribute 即可。

FromStr则提供了从字符串切片&str 向其他类型转换的能力。

js 复制代码
pub trait FromStr {
    type Err;
    fn from_str(s: &str) - >Result < Self,
    Self: :Err > ;
}

因为这个转换过程可能出错,所以from_str 方法的返回类型被设计为Result。正是因为有了这个trait, 所 以str类型才有了一个成员方法parse:

js 复制代码
pub fn parse<F:FromStr>(&self)->Result<F,F::Err>{ ... }

所以我们可以写下面这样非常清晰直白的代码:

js 复制代码
let four ="4".parse::<u32>();
assert_eq!(Ok(4),four);
相关推荐
你才是向阳花2 小时前
如何用Python实现飞机大战小游戏
开发语言·python·pygame
合作小小程序员小小店2 小时前
web网页开发,在线%商城,电商,商品购买%系统demo,基于vscode,apache,html,css,jquery,php,mysql数据库
开发语言·前端·数据库·mysql·html·php·电商
草莓熊Lotso2 小时前
C++ 方向 Web 自动化测试实战:以博客系统为例,从用例到报告全流程解析
前端·网络·c++·人工智能·后端·python·功能测试
星释2 小时前
Rust 练习册 :Phone Number与电话号码处理
开发语言·机器学习·rust
one year.2 小时前
Linux:线程同步与互斥
java·开发语言
一 乐3 小时前
旅游|内蒙古景点旅游|基于Springboot+Vue的内蒙古景点旅游管理系统设计与实现(源码+数据库+文档)
开发语言·前端·数据库·vue.js·spring boot·后端·旅游
JaguarJack3 小时前
15 个 Eloquent 高级技巧,瞬间提升你的 Laravel 应用性能
后端·php·laravel
YDS8293 小时前
苍穹外卖 —— Spring Cache和购物车功能开发
java·spring boot·后端·spring·mybatis
苍老流年3 小时前
1. SpringBoot初始化器ApplicationContextInitializer使用与源码分析
java·spring boot·后端