为什么“一种类型比另一种更通用"?在包含闭包的选项中? [英] Why is "one type is more general than the other" in an Option containing a closure?

查看:27
本文介绍了为什么“一种类型比另一种更通用"?在包含闭包的选项中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些编译好的代码,然后我把 T 变成了 Option 现在我得到这个错误:

I wrote some code that compiled fine, then I turned T into Option<T> and now I'm getting this error:

error[E0308]: mismatched type...one type is more general than the other

在构建最小案例时,我注意到如果我将 data: &str 更改为 data: String 代码再次编译正常.

When building a minimal case I noticed that if I change data: &str into data: String the code compiles fine again.

游乐场链接

pub trait Subscriber {
    fn recv(&mut self, data: &str);
}

pub struct Task<T>(pub Option<T>)
where
    T: Fn(&str);

impl<T> Subscriber for Task<T>
where
    T: Fn(&str),
{
    fn recv(&mut self, data: &str) {
        self.0.take().unwrap()(data)
    }
}

fn main() {
    Task(Some(|_| ()));
}

有人能帮我了解这里发生了什么吗?

Can someone help me understand what's going on here?

推荐答案

您似乎在编译器中遇到了一个怪癖.马桶盖 (|_|()) 泛化为一个函数,该函数对其输入类型 (<T> Fn(T)) 是通用的,但这种方式在 T 的生命周期内不能满足预期的约束.由于for<'a>的限制,我们在这里需要的是一个在输入的生命周期内通用的函数.TaskSubscriber实现中的Fn(&'a str).

You seem to have hit a quirk in the compiler. The toilet closure (|_|()) generalizes into a function which is generic over its input type (<T> Fn(T)), but in such a way that does not fulfill the intended constraint over the lifetime of T. What we need here is a function that is generic over the lifetime of the input due to the bound for<'a> Fn(&'a str) in the implementation of Subscriber for Task<T>.

(由于当前的生命周期省略规则,Subscriber 中排名较高的 trait bound 可以在不明确指示 for<'a> 的情况下编写.进行了这种扩展为了清楚起见,在上一段中.)

(Due to the current lifetime elision rules, the higher-ranked trait bound in Subscriber can be written without explicitly indicating for<'a>. This expansion was made in the previous paragraph for clarity.)

如果您尝试将 Task 值实际用作 Subscriber,编译器会更清楚:

This is made a bit clearer by the compiler if you try to actually use the Task value as a Subscriber:

let mut task = Task(Some(|_| ()));
task.recv("Message");

错误信息:

error[E0599]: no method named `recv` found for struct `Task<[closure@src/main.rs:19:30: 19:36]>` in the current scope
  --> src/main.rs:20:10
   |
5  | / pub struct Task<T>(pub Option<T>)
6  | | where
7  | |     T: Fn(&str);
   | |                -
   | |                |
   | |________________method `recv` not found for this
   |                  doesn't satisfy `_: Subscriber`
...
19 |       let mut task = Task(Some(|_| ()));
   |                                ------
   |                                |
   |                                doesn't satisfy `<_ as std::ops::FnOnce<(&str,)>>::Output = ()`
   |                                doesn't satisfy `_: std::ops::Fn<(&str,)>`
20 |       task.recv("Message");
   |            ^^^^ method not found in `Task<[closure@src/main.rs:19:30: 19:36]>`
   |
   = note: the method `recv` exists but the following trait bounds were not satisfied:
           `<[closure@src/main.rs:19:30: 19:36] as std::ops::FnOnce<(&str,)>>::Output = ()`
           which is required by `Task<[closure@src/main.rs:19:30: 19:36]>: Subscriber`
           `[closure@src/main.rs:19:30: 19:36]: std::ops::Fn<(&str,)>`
           which is required by `Task<[closure@src/main.rs:19:30: 19:36]>: Subscriber`

通过明确地将闭包的单个输入参数标记为引用,闭包将不再泛化参数类型 T,而是泛化到引用输入 的更高级别的生命周期&'a _ 中的一个.

By explicitly marking the closure's single input parameter as a reference, the closure will then no longer generalize over the parameter type T, but over a higher-ranked lifetime of the reference input 'a in &'a _.

let mut task = Task(Some(|_: &_| ()));
task.recv("Message");

游乐场.

虽然并非不可能,但调整编译器以成功推断原始闭包的正确类型可能需要对当前的单态化规则进行重大更改.

While not impossible, adjusting the compiler to successfully infer the right type for the original closure would possibly require substantial changes to the current monomorphization rules.

另见:

这篇关于为什么“一种类型比另一种更通用"?在包含闭包的选项中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆