为什么将易错函数的泛型结果作为泛型参数传递 infer ()?不应该是暧昧的吗?

kmd*_*eko 6 generics rust

在下面的代码中,我有一个make_t()函数T返回Result. 返回的值(传播错误后)用于创建MyTypeusingFrom实现。据我所知,这应该是不明确的,因为没有任何暗示应该选择一种特定的中间类型String或其他类型,但它的选择没有错误(playground):()()

struct Error;
struct MyType;

impl From<String> for MyType {
    fn from(_: String) -> Self {
        println!("used From<String>");
        Self
    }
}

impl From<()> for MyType {
    fn from(_: ()) -> Self {
        println!("used From<()>");
        Self
    }
}

fn make_t<T: Default>() -> Result<T, Error> {
    Ok(T::default())
}

impl MyType {
    fn new() -> Result<Self, Error> {
        Ok(MyType::from(make_t()?))
    }
}

fn main() {
    let _ = MyType::new();
}
Run Code Online (Sandbox Code Playgroud)
struct Error;
struct MyType;

impl From<String> for MyType {
    fn from(_: String) -> Self {
        println!("used From<String>");
        Self
    }
}

impl From<()> for MyType {
    fn from(_: ()) -> Self {
        println!("used From<()>");
        Self
    }
}

fn make_t<T: Default>() -> Result<T, Error> {
    Ok(T::default())
}

impl MyType {
    fn new() -> Result<Self, Error> {
        Ok(MyType::from(make_t()?))
    }
}

fn main() {
    let _ = MyType::new();
}
Run Code Online (Sandbox Code Playgroud)

更令人困惑的是,编译器似乎非常有信心它应该使用(),因为如果我完全删除它From<()>,它确实会产生错误(playground):

used From<()>
Run Code Online (Sandbox Code Playgroud)

难题之一肯定是Result因为转换make_t为无误会使其像预期的那样模棱两可(游乐场):

fn make_t<T: Default>() -> T {
    T::default()
}

impl MyType {
    fn new() -> Result<Self, Error> {
        Ok(MyType::from(make_t()))
    }
}
Run Code Online (Sandbox Code Playgroud)
error[E0277]: the trait bound `MyType: From<()>` is not satisfied
  --> src/main.rs:17:25
   |
17 |         Ok(MyType::from(make_t()?))
   |            ------------ ^^^^^^^^^ the trait `From<()>` is not implemented for `MyType`
   |            |
   |            required by a bound introduced by this call
   |
   = help: the trait `From<String>` is implemented for `MyType`
Run Code Online (Sandbox Code Playgroud)

当然,使用涡轮鱼来指定类型可以避免()(即make_t::<String>()),但我认为无论如何都应该强制执行。

谁能解释一下吗?为什么编译器()在应该不明确的时候选择它?

kmd*_*eko 2

这似乎是一个错误。

存在问题 #94902:Try 特征导致 str::parse 推断报告此问题的单元类型 ()。从那里的评论来看,这似乎是?自 1.13 中引入以来运算符的行为。但是,我在原始RFC #0243或随附的问题和 PR中找不到任何内容来表明这种行为是故意的(尽管我的搜索并不详尽)。

然而,正如@eggyal 和@ChayimFriedman 在评论中所建议的,有一个指向!never类型)的链接。在引入之前,发散的块(例如将生成的块return?将回退到()(单位类型)。它一度稳定下来,但由于这种“后备”类型的机制不完整而被恢复。我不确定它到底是如何表现的,但上面的行为可能是该潜在问题的残余影响。有关更多信息,请参阅从不键入跟踪问题以及链接的问题和 PR。

显然,这种回退()应该会触发警告,但还有另一个错误,这意味着自 1.24 以来就没有出现这种情况,并且它的设计至少部分是为了解决上述问题。我能够确认旧版本的编译器用于在我的代码中发出错误:

error: code relies on type inference rules which are likely to change
   --> src/main.rs:255:25
    |
255 |         Ok(MyType::from(make_some_t()?))
    |                         ^^^^^^^^^^^
    |
    = note: #[deny(resolve_trait_on_defaulted_unit)] on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #39216 <https://github.com/rust-lang/rust/issues/39216>
Run Code Online (Sandbox Code Playgroud)

鉴于?外部用户花了很长时间才报告这一奇怪现象,我很清楚这不是一个常见问题。希望将来可以通过稳定 never 类型或其他一些努力来解决这个问题。