Почему отображение результата из анализа строки с использованием Into :: в невозможность определить тип? - PullRequest
0 голосов
/ 03 июля 2018

Следующее ( ссылка на игровую площадку )

#[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 голосов
/ 03 июля 2018

Относительно вашей второй ошибки: Немного уменьшив ваш пример, заменив результат, полученный при разборе другой пользовательской ошибкой (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

    компилирует: let addr = addr.map_err(|e| {let e2: MyError2 = e.into(); e2})?;

  • MyError2 -> MyError -> MyError

    компилирует: let addr = addr.map_err(|e| {let e2: MyError = e.into(); e2})?;

0 голосов
/ 03 июля 2018

Анализ строки может вернуть любое количество типов :

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 не знает достаточно / старается изо всех сил, чтобы заставить работать определенные типы вывода. Возможно, что переключение на мел сделает систему типов более математически строгой.


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

Это можно написать проще:

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

Мне даже не нужно отображать ошибку

Да, потому что ? делает это для вас.

...