Rust 编译器抱怨两个几乎相同的闭包

Rez*_*eza 0 rust

我有一个带有 2 个哈希集的简单结构:

pub struct IpAddresses {
    pub ipv4s: HashSet<String>,
    pub ipv6s: HashSet<String>,
}
Run Code Online (Sandbox Code Playgroud)

然后是一个简单的函数,它应该为其中一个集合提供迭代器:

  pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
        
       
        if ipv6 {
            self
            .ipv6s
            .iter()
            .filter_map(|a| IpAddr::from_str(a).ok())
        } else {
            self
            .ipv4s
            .iter()
            .filter_map(|a| IpAddr::from_str(a).ok())
        }
    }

Run Code Online (Sandbox Code Playgroud)

我收到以下关于使用框的建议的错误:

error[E0308]: `if` and `else` have incompatible types
   --> src/models/ip_address.rs:131:13
    |
125 |   /         if ipv6 {
126 |   |             self
    |  _|_____________-
127 | | |             .ipv6s
128 | | |             .iter()
129 | | |             .filter_map(|a| IpAddr::from_str(a).ok())
    | |_|_____________________________________________________- expected because of this
130 |   |         } else {
131 | / |             self
132 | | |             .ipv4s
133 | | |             .iter()
134 | | |             .filter_map(|a| IpAddr::from_str(a).ok())
    | |_|_____________________________________________________^ expected closure, found a different closure
135 |   |         }
    |   |_________- `if` and `else` have incompatible types
    |
    = note: expected type `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure@src/models/ip_address.rs:129:25: 129:53]>`
             found struct `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure@src/models/ip_address.rs:134:25: 134:53]>`
    = note: no two closures, even if identical, have the same type
    = help: consider boxing your closure and/or using it as a trait object
help: you could change the return type to be a boxed trait object
    |
122 |     pub fn shared2(&self, ipv6: bool) -> Box<dyn Iterator<Item = IpAddr> + '_> {
    |                                          ~~~~~~~                             +
help: if you change the return type to expect trait objects, box the returned expressions
    |
126 ~             Box::new(self
127 |             .shared_ipv6s
128 |             .iter()
129 ~             .filter_map(|a| IpAddr::from_str(a).ok()))
130 |         } else {
131 ~             Box::new(self
Run Code Online (Sandbox Code Playgroud)

有趣的是,如果我将其中一个翼复制粘贴到函数中,编译器可以正常工作,没有任何错误或不需要 Box:

error[E0308]: `if` and `else` have incompatible types
   --> src/models/ip_address.rs:131:13
    |
125 |   /         if ipv6 {
126 |   |             self
    |  _|_____________-
127 | | |             .ipv6s
128 | | |             .iter()
129 | | |             .filter_map(|a| IpAddr::from_str(a).ok())
    | |_|_____________________________________________________- expected because of this
130 |   |         } else {
131 | / |             self
132 | | |             .ipv4s
133 | | |             .iter()
134 | | |             .filter_map(|a| IpAddr::from_str(a).ok())
    | |_|_____________________________________________________^ expected closure, found a different closure
135 |   |         }
    |   |_________- `if` and `else` have incompatible types
    |
    = note: expected type `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure@src/models/ip_address.rs:129:25: 129:53]>`
             found struct `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure@src/models/ip_address.rs:134:25: 134:53]>`
    = note: no two closures, even if identical, have the same type
    = help: consider boxing your closure and/or using it as a trait object
help: you could change the return type to be a boxed trait object
    |
122 |     pub fn shared2(&self, ipv6: bool) -> Box<dyn Iterator<Item = IpAddr> + '_> {
    |                                          ~~~~~~~                             +
help: if you change the return type to expect trait objects, box the returned expressions
    |
126 ~             Box::new(self
127 |             .shared_ipv6s
128 |             .iter()
129 ~             .filter_map(|a| IpAddr::from_str(a).ok()))
130 |         } else {
131 ~             Box::new(self
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这是内部块的复制粘贴。为什么会发生这种情况?为什么这两个相同的块在第一个实例中并不相同,但只是将它们放入一个函数中就使它们相同了?

Tho*_*mas 6

每个闭包都有自己的匿名类型。即使闭包具有相同的调用签名,即使它们都没有借用任何东西,因此签名中没有生命周期,但这些类型并不相同!

<F>因此,返回的结构中的泛型FilterMap在每个分支中具有不同的类型if,从而导致有关尝试返回不兼容类型的错误消息。

请注意,-> impl Iterator告诉编译器您要返回某种实现 的类型Iterator,但每次它都必须是静态相同的类型,在编译时确定。

当您提取filter_map对单独函数的调用时,只有一个闭包,因此从函数返回一种类型。由于这两个分支的类型相同if,因此问题就消失了。

如果将闭包分配给变量,它也会消失,因为在这两种情况下它的类型也相同:

    pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
        let from_str = |a: &String| IpAddr::from_str(a).ok();
        if ipv6 {
            self
            .ipv6s
            .iter()
            .filter_map(from_str)
        } else {
            self
            .ipv4s
            .iter()
            .filter_map(from_str)
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 谢谢@Thomas,我已经多次阅读你的回复,但我仍然无法理解它。当你说每个闭包都有自己的匿名类型时,那个匿名类型是什么?两个翅膀的返回类型是相同的,编译器可以看到这一点。为什么编译器在类型显而易见的情况下分配匿名类型?将 2 个匿名类型放入 2 个单独的盒子中如何使它们相同?i32 的盒子与 i16 的盒子不是同一类型。 (2认同)