rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.23k stars 12.7k forks source link

bad diagnostic on using `impl Trait` in recursive function #97686

Open rylev opened 2 years ago

rylev commented 2 years ago

Given the following code: [link]

struct Tree<'a> {
    nested: std::collections::BTreeMap<&'a str, Tree<'a>>,
}

impl<'a> Tree<'a> {
    fn flatten(&self) -> impl Iterator<Item = &Tree<'a>> {
        std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten()))
    }
}

The current output on stable is:

error: hidden type `impl Iterator<Item = &Tree<'a>>` differed from previous `std::iter::Chain<std::iter::Once<&Tree<'a>>, FlatMap<std::collections::btree_map::Values<'_, &str, Tree<'a>>, impl Iterator<Item = &Tree<'a>>, [closure@src/lib.rs:7:67: 7:88]>>`
 --> src/lib.rs:7:9
  |
7 |         std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten()))
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
note: previous hidden type bound here
 --> src/lib.rs:7:9
  |
7 |         std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten()))
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

and on nightly:

error: concrete type differs from previous defining opaque type use
 --> src/lib.rs:7:9
  |
7 |         std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten()))
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |         |
  |         expected `std::iter::Chain<std::iter::Once<&Tree<'a>>, FlatMap<std::collections::btree_map::Values<'_, &str, Tree<'a>>, impl Iterator<Item = &Tree<'a>>, [closure@src/lib.rs:7:67: 7:88]>>`, got `impl Iterator<Item = &Tree<'a>>`
  |         this expression supplies two conflicting concrete types for the same opaque type

Ideally this code should compile, but if it cannot be made to compile than the compiler error should use lesson compiler jargon (e.g., "opaque type").

wwylele commented 2 years ago

The code cannot compile because, from different points of view, the function is doing either of the following 2 things


One way to work around this is to introduce a type-erasing dynamic dispatch somewhere in the recursion, so the return type becomes finite. For example:

struct Tree<'a> {
    nested: std::collections::BTreeMap<&'a str, Tree<'a>>,
}

impl<'a> Tree<'a> {
    fn flatten(&self) -> impl Iterator<Item = &Tree<'a>> {
        Box::new(std::iter::once(self).chain(self.nested.values().flat_map(|tree| tree.flatten())))
            as Box<dyn Iterator<Item = &Tree<'a>>>
    }
}
wwylele commented 2 years ago

The error message indeed looks confusing on a recursive function. Maybe some special case error message and advises could be better for recursive function.