Rust学习之——From Trait和Into Trait以及“类型”到字符串的转换

一次奇怪的实践

今天看了一个demo,为自定义类型到String类型的转换实现了From Trait下的from方法,但是是调用into方法来执行该转换。一开始就觉得很离谱。具体如下所示:

struct OriginType{
	value: String,
}

impl From<OriginType> for TargetType {
	fn from (t: OriginType) -> TargetType  {
		省略
	}
}

let o: OriginType = OriginType::new("asdad");
let t: TargetType = o.into();

然后查了资料,发现From和Into这两个Trait是一对,经常在一起出现,但是为什么实现from方法却可以直接用into调用呢?

于是我提出了如下的几个问题:

  1. 为什么可以为类型实现From中的from方法,却用的是从来没有出现过的into方法?
  2. 那么Into Trait是干什么用的?

先回答第一个问题

Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.

One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into thanks to the blanket implementation in the standard library.

根据官网的描述,From和Into这两个Trait是互为相反的(reciprocal)。并且人们应该更喜欢优先实现From,因为实现From会自动提供Into的实现,这要归功于标准库中的一揽子(blanket)实现。

第二个问题

Only implement Into when targeting a version prior to Rust 1.41 and converting to a type outside the current crate. From was not able to do these types of conversions in earlier versions because of Rust’s orphaning rules. See Into for more details.

Into Trait只在Rust1.41之前,并且用于把一个类型转换为当前crate之外的类型(例如标准库中的Vec或外部库中的类型)。这是因为Rust有孤儿规则,不能去改动外部的,或者说是当前crate之外的类型,而From在早期的版本中无法做到这些类型之间的转换。

Prefer using Into over using From when specifying trait bounds on a generic function. This way, types that directly implement Into can be used as arguments as well.

当在泛型函数上指定Trait bounds时,应该优先使用Into。稍后将在Into的用法中详细介绍。

From Trait

From Trait 用于提供值与值之间的转换(从类型OriginType到另一个类型TargetType)。

pub trait From<T> {
    fn from(T) -> Self;
}

要为TargetType实现From,必须实现的方法是from。看下面的一个例子:

impl From<OriginType> for TargetType {
	fn from(value: OriginType) -> Self {
		// pass
		// 返回一个TargetType的实例
	}
}

let o: OriginType;
// 这两种方法都可以,第一种方法可以自动推断类型,因此不需要类型注解
// 第二种方法由于使用了into方法,而且一个类型可以转换为多个其他类型,因此必须进行类型注解
let t = TargetType::from(o);
let t: TargetType = o.into();

实例:下面的代码片段就是从字符串的HTTP请求 转换为 为带有数据结构的、便于查询的HttpRequest的结构体类型。

impl From<String> for HttpRequest {
	fn from (req: String) -> Self {
		return HttpRequest {
			method: parsed_method(req),
			resource: parsed_resource(req),
			version: parsed_version(req),
			headers: parsed_headers(req),
			body: parsed_body(req),
		}
	}
}

let req_string: String = String::from_utf8(read_buffer.to_vec()).unwrap();
// 有两种方法第一种是实现的from方法,第二种是自动对应的Into实现的into方法
// 第一种方法可以自动推断类型,因此可以不写类型注解
// 第二种方法使用了into方法,而且一个类型可以转换为多个其他类型,因此必须进行类型注解
let http_request = HttpRequest::from(req_string);
let http_request: HttpRequest = req_string.into();

再说Into Trait

之前说了,在Rust1.41之前,如果目标类型不是当前crate中的一部分,那么不能直接实现From方法,例如使用以下的代码:

在下面的代码片段中,Wrapper是一个自定义的元组结构体,用于存放类型T的向量,而目标类型Vec是标准库中的类型。

struct Wrapper<T>(Vec<T>);

impl<T> From<Wrapper<T>> for Vec<T> {
    fn from(w: Wrapper<T>) -> Vec<T> {
        w.0
    }
}

这段代码在旧版本中会编译失败,这是因为Rust的孤儿规则曾经更严格一些。要绕过这个,可以尝试实现Into:

impl Into<TargetType> for OriginType {
    fn into(value: OriginType) -> TargetType {
        // 返回TargetType类型的值
    }
}
struct Wrapper<T>(Vec<T>);

impl<T> Into<Vec<T>> for Wrapper<T> {
    fn into(self) -> Vec<T> {
        self.0
    }
}

重要的是要了解 Into 不提供 From 实现( From 却提供 Into)。因此,应该始终尝试先实现 From,如果 From 无法实现,则再尝试使用 Into

标准库中的String类型就实现了Into>:is_hello的参数表示可以接收任意一个能够转换为Vec的类型。

fn is_hello<T: Into<Vec<u8>>>(s: T) {
   let bytes = b"hello".to_vec();
   assert_eq!(bytes, s.into());
}

let s = "hello".to_string();
is_hello(s);

注意事项

From和Into实现的转换不允许出现错误,即转换过程不能抛出异常。如果转换过程允许抛出异常请使用:TryFrom和TryInto。

你可能感兴趣的:(rust,rust)