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类型实现了Fromtrait。我们可以发现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类型实现Fromtrait,可以让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构造的一个结构体实例。
上面就是这一节的学习内容了,有机会我得重新多学几遍!!!
