rust 将特征边界应用于关联类型

jvidinwx  于 2023-03-08  发布在  其他
关注(0)|答案(2)|浏览(144)

假设我有这样一个特质:

trait InternalError {
    fn internal(error: String) -> Self;
}

如果我想在函数中使用它,我可以这样做:

struct MyU16(pub u16);

fn my_try_from<E: InternalError>(value: u32) -> Result<MyU16, E> {
    if value < u16::MAX as u32 {
        Ok(MyU16(value as u16))
    } else {
        Err(E::internal("invalid".to_string()))
    }
}

This works just fine.
现在假设我不使用自己的函数,而是使用TryFrom

impl<E: InternalError> TryFrom<u32> for MyU16 {
    type Error = E;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        if value < u16::MAX as u32 {
            Ok(MyU16(value as u16))
        } else {
            Err(E::internal("invalid".to_string()))
        }
    }
}

我尝试了各种不同的表达方式,但我不知道如何表达"在错误情况下,this TryFrom impl返回impls InternalError的内容"。
我该怎么做呢?

9gm1akwq

9gm1akwq1#

在错误情况下,此TryFrom实现将返回暗示InternalError的内容
TryFrom希望在编译时知道返回的错误类型,而您似乎希望在运行时决定类型。
假设FirstErrorSecondError都实现了InternalError
在这种情况下

impl TryFrom<u32> for MyU16 {
    type Error = FirstError;

    fn try_from(value: u32) -> Result<Self, FirstError> {
        /*...*/
    }
}

将编译,但当然try_from只能返回FirstError
如果你想在运行时在FirstErrorSecondError之间做出决定,那么相关的错误type(不是trait)需要能够同时包含这两个错误。

impl TryFrom<u32> for MyU16 {
    type Error = Box<dyn InternalError>;

    fn try_from(value: u32) -> Result<Self, Box<dyn InternalError>> {
        /*...*/
        Box::new(FirstError::internal("invalid".to_string()))
    }
}

这也暗示了为什么enum是Rust中表示错误的惯用方法:通常你在编译时知道什么可能出错,但你还不知道什么出错(如果有的话)。实际发生的错误只有在运行时才知道。因此,让类型能够表示一组潜在的错误,而示例是一个特定的错误是一个好主意。
enum迭代符合这一要求,它向用户清楚地说明了可能出错的地方,并且在许多情况下允许您创建错误而无需额外的堆分配。

lsmd5eda

lsmd5eda2#

它不能编译的原因是impl声明中的type参数必须出现在 implementing typeimpl<T> Foo<T>)或 implemented traitimpl<T> MyTrait<T> for Foo)中,你不能只写impl<T> for Foo,然后在impl代码块中再次提到T
正如扩展的错误所建议的那样,有一种变通方法可以将幻像类型引入到实现类型中,但您可能不喜欢它使代码看起来的样子:

use std::marker::PhantomData;

struct MyU16<T>(pub u16, PhantomData<T>);

impl<E: InternalError> TryFrom<u32> for MyU16<E> {
    type Error = E;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        if value < u16::MAX as u32 {
            Ok(MyU16(value as u16, PhantomData))
        } else {
            Err(E::internal("invalid".to_string()))
        }
    }
}

相关问题