今天看了一个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调用呢?
于是我提出了如下的几个问题:
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 用于提供值与值之间的转换(从类型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();
之前说了,在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。