为什么使用Into :: in解析字符串映射结果到无法推断类型?

时间:2018-07-03 13:15:31

标签: rust type-inference

以下(playground link

#[derive(Debug)]
struct MyError();

impl From<::std::net::AddrParseError> for MyError {
    fn from(_e: ::std::net::AddrParseError) -> MyError {
        MyError()
    }
}

fn accept_addr(_addr: ::std::net::SocketAddr) {}

fn main() -> Result<(), MyError> {
    let addr = "127.0.0.1:23456".parse();
    let addr = addr.map_err(|e| e.into())?;
    Ok(accept_addr(addr))
}

不起作用。错误:

error[E0282]: type annotations needed
  --> src/main.rs:14:30
   |
14 |     let addr = addr.map_err(|e| e.into())?;
   |                              ^ consider giving this closure parameter a type

我无法按照上面错误消息的建议解决此问题。如果我将代码更改为:

let addr = addr.map_err(|e: ::std::net::AddrParseError| e.into())?;

我遇到另一个错误:

error[E0282]: type annotations needed
  --> src/main.rs:14:16
   |
14 |     let addr = addr.map_err(|e: ::std::net::AddrParseError| e.into())?;
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `_`

我的解决方案是改用From

let addr = addr.map_err(|e| <MyError as From<_>>::from(e))?; // Worked!!

更好的是,我后来发现甚至不需要映射错误:

let addr = "127.0.0.1:23456".parse()?;
Ok(accept_addr(addr))

我知道类型推断从来都不容易,但是上面的代码为什么不能正确地推断类型?

2 个答案:

答案 0 :(得分:3)

关于第二个错误:通过将解析后的结果替换为另一个自定义错误(MyError2)来简化示例,这会再次产生相同的问题(|e: MyError2| e.into()仍然没有帮助):

#[derive(Debug)]
struct MyError();
struct MyError2();

impl From<MyError2> for MyError {
    fn from(_e: MyError2) -> MyError{
        MyError()
    }
}

fn main() -> Result<(),MyError> {
    let addr = Err(MyError2{});
    addr.map_err(|e| e.into())?;
    Ok(())
}
error[E0282]: type annotations needed
  --> src/main.rs:20:5
   |
20 |     addr.map_err(|e| e.into())?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `_`

error: aborting due to previous error

用标准库中的宏定义(?替换try!会显示:

macro_rules! try {
    ($e:expr) => (match $e {
        Ok(val) => val,
        Err(err) => return Err(::std::convert::From::from(err)),
    });
}

fn main() -> Result<(),MyError> {
    let addr = Err(MyError2{});
    let addr = try!(addr.map_err(|e| e.into()));
    Ok(addr)
}
error[E0282]: type annotations needed
  --> src/main.rs:14:13
   |
14 |         Err(err) => return Err(::std::convert::From::from(err)),
   |             ^^^ cannot infer type for `_`
...
20 |     let addr = try!(addr.map_err(|e| e.into()));
   |                -------------------------------- in this macro invocation

在宏定义中From::from()上应用的err导致推断失败。将此行替换为:Err(err) => return Err(::std::convert::From::from(err)),Err(err) => return Err(err),解决了该问题-程序得以编译。

原因是,通过在From::from()MyError之间通过MyError2进行两次转换,此转换管道变得模棱两可。编译器无法确定中间类型。

示例-两个有效选项(注意From::from是自反实现的):

  • MyError2-> MyError2-> MyError

    编译:{{1​​}}

  • let addr = addr.map_err(|e| {let e2: MyError2 = e.into(); e2})?;-> MyError2-> MyError

    编译:{{1​​}}

答案 1 :(得分:2)

Parsing a string can return any number of types

pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err>
where
    F: FromStr, 

您的代码没有任何提示,提示在开始弄乱 之前应该从字符串中解析出什么,因此编译器不知道选择哪种parse实现:

let addr = "127.0.0.1:23456".parse();
let addr = addr.map_err(|e| e.into())?;

然后,您尝试将未知类型转换为MyError,从而导致错误。

let addr = addr.map_err(|e: ::std::net::AddrParseError| e.into())?;

仅知道 error 类型是不够的,因为多种类型可以实现具有相同错误类型的FromStr

pub trait FromStr {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

这意味着编译器仍然不知道解析为什么。

通常,编译器不会进行多次推理,因为搜索空间可能成倍增大。在其他情况下,当前的 ad hoc 特征系统不够了解/很难尝试使某些类型的推理工作。 switching to chalk可能会使类型系统在数学上更加严格。


let addr = addr.map_err(|e| <MyError as From<_>>::from(e))?;

这可以写得更简单:

let addr = addr.map_err(|e| MyError::from(e))?;
  

我什至不需要映射错误

是的,因为?为您做到了。