【问题标题】:Trait implementation not being inferenced correctly when defined via macro通过宏定义时未正确推断特征实现
【发布时间】:2020-09-24 19:59:50
【问题描述】:

我一直在使用json crate(GitHubcrates.io),我决定实现一个辅助函数来解析 json 中的数字:

use std::convert::{TryInto, From, Infallible};
use json::number::Number;
use json::{self, JsonValue, JsonError};
use std::fmt;
use std::error::Error;

#[derive(Debug)]
enum ConvertError {
    JsonError(JsonError),
    InfallibleError(Infallible),
    InvalidTypeError
}
impl fmt::Display for ConvertError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::JsonError(e) => e.fmt(f),
            Self::InfallibleError(e) => e.fmt(f),
            Self::InvalidTypeError => write!(f, "Field has an invalid type")
        }
    }
}
impl Error for ConvertError {}
impl From<JsonError> for ConvertError {
    fn from(e: JsonError) -> Self {
        Self::JsonError(e)
    }
}
impl From<Infallible> for ConvertError {
    fn from(e: Infallible) -> Self {
        Self::InfallibleError(e)
    }
}

fn test_num<T: From<Number>>(obj: JsonValue) -> Result<T, ConvertError> {
    match obj {
        JsonValue::Number(num) => Ok(num.try_into()?),
        _ => Err(ConvertError::InvalidTypeError)
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let s = "{\"test_float\": 0.1, \"test_int1\": 1, \"test_int2\": 2}";
    let mut parsed = json::parse(s)?;

    // Works (From<f32> defined here: https://docs.rs/json/0.12.4/src/json/number.rs.html#344-361)
    let num1: f32 = test_num(parsed["test_float"].take())?;
    // Also works
    let num2: i32 = match parsed["test_int1"].take() {
        JsonValue::Number(num) => match num.try_into() {
            Ok(n) => n,
            _ => return Err(Box::new(ConvertError::InvalidTypeError))
        },
        _ => return Err(Box::new(ConvertError::InvalidTypeError))
    };
    // Doesn't work (From<i32> defined here: https://docs.rs/json/0.12.4/src/json/number.rs.html#495)
    let num3: i32 = test_num(parsed["test_int2"].take())?;

    println!("Number 1: {}", num1);
    println!("Number 2: {}", num2);
    println!("Number 3: {}", num3);

    Ok(())
}

但是,当我尝试编译程序时,返回以下错误:

error[E0277]: the trait bound `i32: From<json::number::Number>` is not satisfied
  --> src\main.rs:56:21
   |
34 | fn test_num<T: From<Number>>(obj: JsonValue) -> Result<T, ConvertError> {
   |                ------------ required by this bound in `test_num`
...
56 |     let num3: i32 = test_num(parsed["test_int2"].take())?;
   |                     ^^^^^^^^ the trait `From<json::number::Number>` is not implemented for `i32`
   |
   = help: the following implementations were found:
             <i32 as From<NonZeroI32>>
             <i32 as From<bool>>
             <i32 as From<i16>>
             <i32 as From<i8>>
           and 2 others

特别有趣的是let num1: f32 = test_num(parsed["test_float"].take())?; 和内联函数确实没有有编译错误,而i32 变体。 据我所知,From&lt;f32&gt;From&lt;i32&gt; 实现之间的唯一区别是i32 实现了via a macro

这是我使用 trait 实现方式的错误、宏实现方式的错误还是编译器错误?

(我目前正在使用最新的夜间编译器:rustc 1.48.0-nightly (8b4085359 2020-09-23)

【问题讨论】:

  • 宏实现了From&lt;i32&gt; for NumberTryFrom&lt;Number&gt; for i32,但没有实现From&lt;Number&gt; for i32
  • 啊,你说得对,不知道我是怎么误读的,但它打开了一大堆蠕虫,因为TryFrom&lt;Number&gt;::ErrorNumberOutOfScope,它没有实现std::error::Error,所以我无法使用 ? 进行隐式转换,就像我在 main 中所做的那样,一旦我弄清楚到那时是否没有其他人回答它,我会自己回答。

标签: json rust


【解决方案1】:

正如@trentcl 在 cmets 中发布的那样,只有 TryFrom&lt;Number&gt; 用于 i32,而不是 From&lt;Number&gt;。这实际上使事情变得更加复杂,因为TryFrom&lt;Number&gt;::ErrorNumberOutOfScope,它既不实现std::error::Error 也不实现std::fmt::Debug

处理完所有这些问题后,新功能变为:

fn test_num<T: TryFrom<Number>>(obj: JsonValue) -> Result<T, ConvertError<T>> {
    match obj {
        JsonValue::Number(num) => Ok(match num.try_into() {
            Ok(n) => n,
            Err(e) => return Err(ConvertError::TryFromError(e))
        }),
        _ => Err(ConvertError::InvalidTypeError)
    }
}

它可以用于i32和其他号码类型:

let num2: i32 = test_num(parsed["test_int1"].take())?;

完整代码供参考:

use std::convert::{TryInto, TryFrom, Infallible};
use json::number::Number;
use json::{self, JsonValue, JsonError};
use std::fmt;
use std::error::Error;

enum ConvertError<T: TryFrom<Number>> {
    JsonError(JsonError),
    InfallibleError(Infallible),
    TryFromError(T::Error),
    InvalidTypeError
}
impl<T: TryFrom<Number>> fmt::Display for ConvertError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::JsonError(e) => std::fmt::Display::fmt(&e, f),
            Self::InfallibleError(e) => std::fmt::Display::fmt(&e, f),
            Self::TryFromError(_) => write!(f, "TryFrom failed"),
            Self::InvalidTypeError => write!(f, "Field has an invalid type")
        }
    }
}
impl<T: TryFrom<Number>> fmt::Debug for ConvertError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::JsonError(e) => std::fmt::Debug::fmt(&e, f),
            Self::InfallibleError(e) => std::fmt::Debug::fmt(&e, f),
            Self::TryFromError(_) => write!(f, "ConvertError::TryFromError"),
            Self::InvalidTypeError => write!(f, "ConvertError::InvalidTypeError"),
        }
    }
}
impl<T: TryFrom<Number>> Error for ConvertError<T> {}
impl<T: TryFrom<Number>> From<JsonError> for ConvertError<T> {
    fn from(e: JsonError) -> Self {
        Self::JsonError(e)
    }
}
impl<T: TryFrom<Number>> From<Infallible> for ConvertError<T> {
    fn from(e: Infallible) -> Self {
        Self::InfallibleError(e)
    }
}

fn test_num<T: TryFrom<Number>>(obj: JsonValue) -> Result<T, ConvertError<T>> {
    match obj {
        JsonValue::Number(num) => Ok(match num.try_into() {
            Ok(n) => n,
            Err(e) => return Err(ConvertError::TryFromError(e))
        }),
        _ => Err(ConvertError::InvalidTypeError)
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let s = "{\"test_float\": 0.1, \"test_int1\": 1}";
    let mut parsed = json::parse(s)?;

    let num1: f32 = test_num(parsed["test_float"].take())?;
    let num2: i32 = test_num(parsed["test_int1"].take())?;

    println!("Number 1: {}", num1);
    println!("Number 2: {}", num2);

    Ok(())
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-10
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    • 2021-04-13
    • 1970-01-01
    • 2020-04-01
    相关资源
    最近更新 更多