27、From和Into
From and Into
让我们回到字符串之旅的起点!
let ticket = Ticket::new(
"A title".into(),
"A description".into(),
"To-Do".into()
);
芜湖,现在我们终于可以清楚.into()
在这里发挥的作用力(喜!
问题
这是new
方法的签名:
impl Ticket {
pub fn new(
title: String,
description: String,
status: String
) -> Self {
// [...]
}
}
我们还可以看到字符串字面量(比如“Hello Rust!”)属于&str
类型。
如果直接赋值给“Hello Rust!”的话,会出问题:方法期待的参数类型是String
,但是我们提供的却是&str
。这次没有神奇的强制转换来拯救我们,我们必须手动执行转换。
From和Into
Rust标准库定义了两个traits来实现可靠的转换:From
和Into
,在std::convert
模块中。
pub trait From<T>: Sized {
fn from(value: T) -> Self;
}
pub trait Into<T>: Sized {
fn into(self) -> T;
}
这些trait展示了一些我们从来没有见过的概念:supertraits
和implicit trait bounds
。
Supertrait / Subtrait
From:Sized
语法意味着From
是Size
的subtrait
(子trait):任何实现From
的类型必须也实现Sized
。或者说,Sized
是From
的supertrait
。
我感觉官方讲解的不是特别清晰,所以专门去查了一些资料:
supertrait
(超trait):一个 trait 如果需要另一个 trait 的功能才能成立,那么它就“依赖”于那个 trait,称后者为它的 supertrait。subtrait
(子trait)如果某个 trait 显式地要求其父 trait 的条件,那么这个 trait 就是子 trait。
例如:
trait SuperTrait {
fn foo(&self);
}
trait SubTrait: SuperTrait {
fn bar(&self);
}
SubTrait
是SuperTrait
的子 trait。SubTrait: SuperTrait
表示SubTrait
需要先实现SuperTrait
才能成立。
trait Speak {
fn speak(&self);
}
trait LoudSpeak: Speak {
fn loud_speak(&self);
}
struct Dog;
impl Speak for Dog {
fn speak(&self) {
println!("Woof!");
}
}
impl LoudSpeak for Dog {
fn loud_speak(&self) {
println!("WOOF!");
}
}
LoudSpeak
是Speak
的子 trait(Speak
是它的supertrait
)。- 所以,实现
LoudSpeak
的类型必须首先实现Speak
。
在上面的实际例子中,如果只实现LoudSpeak
而不实现 Speak
,编译会失败。
Implicit trait bounds(隐式trait约束)
每当我们使用泛型参数时,编译器都会隐式假定它是Sized
。
比如:
pub struct Foo<T> {
inner: T,
}
实际上等同于:
pub struct Foo<T: Sized>
{
inner: T,
}
对于From<T>
这种情况,trait定义等同于:
pub trait From<T: Sized>: Sized {
fn from(value: T) -> Self;
}
换句话说,即使前面的约束是隐式的,T
和实现From<T>
的类型都必须是Sized
。
Negative trait bounds(负向trait约束)
我们可以使用negative trait bound取消掉Sized约束,这就是负向trait约束。语法结构如下:
pub struct Foo<T: ?Sized> {
// ^^^^^^^
// This is a negative trait bound
inner: T,
}
这里的语法可以理解为:“T
可能是也可能不是Sized
”,它允许我们将T
绑定到DST(比如Foo<str>
),但这是一个特例,负向trait约束时Sized独有的,我们不能将他们和其他trait一起使用。
&str to String
在std的官方文档里,我们可以看到那些std
类型实现了From
trait。我们可以发现String
为String
实现了From<&str>
。因此,我们可以这样写:
let title = String::from("A title");
不过我们主要使用.into()
。
查看Into
的实现,我们找不到用于&str
的Into<String>
。
这是因为From
和Into
是dual traits
(对偶traits?我查不到官方中文翻译)
具体来说,任何实现了 From
的类型,都可以通过一个泛型实现(blanket implementation)自动获得 Into
*的实现:
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}
如果类型U
实现了From<T>
,那么T
的Into<U>
也会自动实现。这就是为什么我们可以在找不到用于&str
的Into<String>
的情况下编写let title = "A title".into();
。
.into()
每次看到.into()
,我们都见证了类型的转换,但是目标类型是什么?
在大多数情况下,目标类型是:
- 由函数/方法的签名指定(比如之前例子中的
Ticket::new
) - 在变量声明中指定类型注释(比如
let title: String = "A title".into();
)
.into()
只要编译器能从上下文中推断出目标类型而不会产生歧义,那么它就能正常工作。
练习
为u32类型实现From
trait,可以让u32类型调用into()
和from()
,实现类型转换为WrappingU32
代码实现如下:
impl From<u32> for WrappingU32 {
fn from(value: u32) -> Self {
Self { value }
}
}
impl From<u32> for WrappingU32
这句代码,是说,为WarppingU32
类型实现From
trait,里面的from
方法就是具体实现,接受u32
类型的参数,返回Self
,指的是WarppingU32
类型,然后我们返回用Self
构造的一个结构体实例。
上面就是这一节的学习内容了,有机会我得重新多学几遍!!!