rust 使用fn try_parse< T>(value:T)合并< T>不同T的不同TryFrom实现

ukqbszuj  于 2023-03-02  发布在  其他
关注(0)|答案(2)|浏览(209)

假设我有一个结构体调用SevenDigits,顾名思义,它是一个由7位且只有7位数字组成的类型。

struct SevenDigits([u8; 7]);

我已经为三种类型实现了std::convert::TryFrom特征:&strVectoru32。例如,对于u32的实现,它是:

impl TryFrom<u32> for ICDigits {
    type Error = &'static str;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        let mut digits = [0; 7];
        let mut remaining = value;

        for i in (0..7).rev() {
            let digit = remaining % 10;
            remaining /= 10;

            digits[i] = digit as u8;
        }

        if remaining > 0 {
            Err("Number is too large to fit in an array of 7 digits")
        } else {
            Ok(ICDigits(digits))
        }
    }
}

假设现在我想实现一个方法pub fn try_parse<T: TryInto<SevenDigits>>(value: T) -> Result<Self, &'static str> {,其中T是实现TryInto<SevenDigits> trait的任何泛型类型。
这个函数的自然实现是:

pub fn try_parse<T: TryInto<SevenDigits>>(value: T) -> Result<Self, &'static str> {
    TryFrom::try_from(value)
  }
}

cargo check上,这给了我两个错误:

error[E0271]: type mismatch resolving `<SevenDigitsas TryFrom<T>>::Error == &str`
  --> src/digits.rs:25:5
   |
25 |     TryFrom::try_from(value)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Infallible`, found `&str`

error[E0277]: the trait bound `SevenDigits: From<T>` is not satisfied
  --> src/digits.rs:25:5
   |
25 |     TryFrom::try_from(value)
   |     ^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `SevenDigits`
   |
   = note: required because of the requirements on the impl of `Into<SevenDigits>` for `T`
   = note: required because of the requirements on the impl of `TryFrom<T>` for `ICDigits`
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
   |
9  | impl SevenDigits where SevenDigits: From<T> {
   |               +++++++++++++++++++++++

错误信息令人困惑,我该如何实现这个pub fn try_parse函数呢?

fjaof16o

fjaof16o1#

我摆弄了你的例子,直到我成功了:

pub struct SevenDigits([u8; 7]);

pub fn try_parse<T>(value: T) -> Result<SevenDigits, &'static str> 
where
    T: TryInto<SevenDigits, Error = &'static str>,
{
    value.try_into()
}

Playground.
这里不能使用只有trait边界T: TryInto<U>TryFrom::try_from,因为默认实现是:impl<T, U> TryFrom<U> for T where U: Into<T>。您必须直接使用TryInto::try_into。实现TryFrom<T> for U将自动实现TryInto<U> for T,大致如下所示:

impl<T, U> const TryInto<U> for T
where
    U: TryFrom<T>,
{
    type Error = U::Error;

    fn try_into(self) -> Result<U, U::Error> {
        U::try_from(self)
    }
}

这并不意味着当T实现TryInto<U>时可以使用TryFrom<T>,因为这种关系不是自反的,也就是说它只在从TryFromTryInto的一个方向上。
除此之外,您只有一个类型与关联的Error类型不匹配。您必须显式地告诉编译器,TTryInto<SevenDigits>实现返回一个&'static str作为错误,以使其与您的函数的返回类型匹配。

jogvjijk

jogvjijk2#

所以看起来实际上有两个错误。
首先,TryFrom特性意味着TryInto需要您定义一个关联的错误类型。(我假设其余的错误类型)是&'static str。但是,泛型不知道这一点,所以实际的错误类型是T ::Error。要解决这个问题,你可以改变函数的返回类型,或者使用match并自己返回一个字符串错误。
那么,你使用TryIntoTryForm的方式就不起作用了(老实说,我不太清楚为什么)。要解决这个问题,你可以使用try_into()或使用where子句来定义trait绑定SevenDigits: TryFrom<T>
下面是两个行之有效的解决方案示例:
1.更改函数签名并使用TryInto

pub fn try_parse<T: TryInto<SevenDigits>>(value: T) -> Result<Self, T::Error> {
    value.try_into()
}

1.使用特征绑定和TryFrom

pub fn try_parse<T>(value: T) -> Result<Self, &'static str> where SevenDigits: TryFrom<T> {
    match TryFrom::try_from(value) {
        Ok(seven_digits) => Ok(seven_digits),
        Err(_) => Err("Number is too large to fit in an array of 7 digits")
    }
}

相关问题