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来实现可靠的转换:FromInto,在std::convert模块中。

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

pub trait Into<T>: Sized {
    fn into(self) -> T;
}

这些trait展示了一些我们从来没有见过的概念:supertraitsimplicit trait bounds

Supertrait / Subtrait

From:Sized语法意味着FromSizesubtrait(子trait):任何实现From的类型必须也实现Sized。或者说,SizedFromsupertrait

我感觉官方讲解的不是特别清晰,所以专门去查了一些资料:

  • supertrait(超trait):一个 trait 如果需要另一个 trait 的功能才能成立,那么它就“依赖”于那个 trait,称后者为它的 supertrait
  • subtrait(子trait)如果某个 trait 显式地要求其父 trait 的条件,那么这个 trait 就是子 trait。

例如:

trait SuperTrait {
    fn foo(&self);
}
trait SubTrait: SuperTrait {
    fn bar(&self);
}
  • SubTraitSuperTrait 的子 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!");
    }
}
  • LoudSpeakSpeak 的子 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。我们可以发现StringString实现了From<&str>。因此,我们可以这样写:

let title = String::from("A title");

 

不过我们主要使用.into()

查看Into的实现,我们找不到用于&strInto<String>

 

这是因为FromIntodual 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>,那么TInto<U>也会自动实现。这就是为什么我们可以在找不到用于&strInto<String>的情况下编写let title = "A title".into();

.into()

每次看到.into(),我们都见证了类型的转换,但是目标类型是什么?

在大多数情况下,目标类型是:

  • 由函数/方法的签名指定(比如之前例子中的Ticket::new
  • 在变量声明中指定类型注释(比如let title: String = "A title".into();

 

.into() 只要编译器能从上下文中推断出目标类型而不会产生歧义,那么它就能正常工作。

练习

image-20250910225607373

为u32类型实现Fromtrait,可以让u32类型调用into()from(),实现类型转换为WrappingU32

代码实现如下:

impl From<u32> for WrappingU32 {
    fn from(value: u32) -> Self {
        Self { value }
    }
}

image-20250910225741491

impl From<u32> for WrappingU32这句代码,是说,为WarppingU32类型实现From trait,里面的from方法就是具体实现,接受u32类型的参数,返回Self,指的是WarppingU32类型,然后我们返回用Self构造的一个结构体实例。

 

上面就是这一节的学习内容了,有机会我得重新多学几遍!!!

 

阅读剩余
THE END