3 ms·
I'm out of the loop. How will "impl Trait" help with errors? Does it automatically improve errors, or just allow library authors to implement custom error or so
by Buttons840 9y ago
I'm out of the loop. How will "impl Trait" help with errors? Does it automatically improve errors, or just allow library authors to implement custom error or something?
- steveklabnik 9y agoConsider this code: fn foo_no(v: &[i32]) -> std::iter::Cloned<std::slice::Iter<i32>> { v.iter().cloned() } fn foo_impl<'a>(v: &'a [i32]) -> impl Iterator<Item=i32> + 'a { v.iter().cloned() } fn main() { let v = vec![1, 2, 3]; let () = foo_no(&v); let () = foo_impl(&v); } Given the let ()s, this will fail to compile for both. For the first, you get = note: expected type `std::iter::Cloned<std::slice::Iter<'_, i32>>` for the second, you get = note: expected type `impl std::iter::Iterator` The former gets longer and longer based on how many iterator chains you do, the latter will be the same no matter how many you add. It's also just generally way more clear, "I was expecting something implementing Iterator, and I didn't get it" vs "I was expecting this exact chain of nested iterators".
- Buttons840 9y agoOk. It looks like the new feature allows library authors to express their intentions with simpler code, and thus error messages will also be simpler. Looks good, thanks for taking the time to answer my question.
- steveklabnik 9y agoNo problem! It also does enable some other things too. For example, closures have a type that's not writable, so fn foo() -> impl Fn(i32) -> i32 { |x| x + 1 } can be written, since closures implement the Fn trait, but there's no way to write the type directly.
- mike-cardwell 9y agoCan you explain why an explicit lifetime is needed in foo_impl? I don't see a connection between the function argument lifetime and the return value lifetime, as the return value is a brand new object with cloned values... I realise I'm missing something fundamental here.
- steveklabnik 9y agookay so, the un-elided version of foo_no is fn foo_no<'a>(v: &'a [i32]) -> std::iter::Cloned<std::slice::Iter<'a, i32>> { I am not sure why the impl Trait version doesn't elide properly; I wrote it without the lifetime first and the compiler told me I need it, so I wrote it out, which is often what happens to me in these situations. I assume, since the elision rules were written years before impl Trait became a thing, that there's some sort of reason why it doesn't elide, but honestly, I'm not sure.
- mike-cardwell 9y agoSo why is the lifetime of the return value linked to the lifetime of the function argument? Couldn't the return value live longer than the function argument in that example?
- steveklabnik 9y agoIn both cases, the iterators are over the slice, so their lifetimes need to be tied together. The iterator can't live longer than the slice, or it'd be a use after free. Does that make sense?
- mike-cardwell 9y agoYes. Thanks for the explanation.
- irishsultan 9y agoThe Cloned iterator clones the elements of the underlying iterator, but only on-demand, so obviously it can't do that once the argument isn't live anymore.