本文主要是介绍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调用呢?
于是我提出了如下的几个问题:
- 为什么可以为类型实现From中的from方法,却用的是从来没有出现过的into方法?
- 那么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
overInto
because implementingFrom
automatically provides one with an implementation ofInto
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. SeeInto
for more details.
Into Trait只在Rust1.41之前,并且用于把一个类型转换为当前crate之外的类型(例如标准库中的Vec或外部库中的类型)。这是因为Rust有孤儿规则,不能去改动外部的,或者说是当前crate之外的类型,而From在早期的版本中无法做到这些类型之间的转换。
Prefer using
Into
over usingFrom
when specifying trait bounds on a generic function. This way, types that directly implementInto
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<T>
是标准库中的类型。
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<Vec<u8>>
:is_hello的参数表示可以接收任意一个能够转换为Vec<u8>
的类型。
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学习之——From Trait和Into Trait以及“类型”到字符串的转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!