5 ms·
ah yeah I've gotten that error[E0308]: `if` and `else` have incompatible types before. I was trying to write a simple little program that would output data
by justinsaccount 6y ago
ah yeah I've gotten that
error[E0308]: `if` and `else` have incompatible types
before.
I was trying to write a simple little program that would output data and optionally reversed (like sort -r). Nothing I could do with iterators would work because it kept trying to tell me that a reverse iterator was not compatible with an iterator. It would work if I hardcoded it, but any code like
output = if (reverse) { result.rev() } else { result };
would fail to compile with a completely nonsense error message. I think I eventually got it to work by collecting it into a vector first and reversing that. Haven't really touched rust since. Honestly the compiler might as well just tell me to go fuck myself.
- estebank 6y ago> would fail to compile with a completely nonsense error message. How long ago was this? We spend a lot of time trying to make the error messages easy to follow and informative. If it wasn't that long ago, I would love to see the exact cases you had trouble with in order to improve them. > I think I eventually got it to work by collecting it into a vector first and reversing that. Collecting and reversing is indeed what I would do if I couldn't procure a reversible iterator (DoubleEndedIterator), but if you have one, you can write the code you wanted by spending some cost on a fat pointer by boxing the alternatives[1]. > Haven't really touched rust since. Honestly the compiler might as well just tell me to go fuck myself. I'm sad to hear that, both because you bounced off (which is understandable) and because that experience goes counter to what we aim for. We dedicate a lot of effort on making errors not only readable, pedagogic and actionable (with varying levels of success). We really don't want the compiler to come across as antagonistic or patronizing. [1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0965d3a326d977d7df1293470396e9ea https://play.rust-lang.org/?version=stable&mode=debug&editio... Edit: For what is worth, type mismatch errors do try to give you appropriate suggestions, but in the case of two types that both implement the same trait (like the one you mention), the compiler does not look for traits that are implemented for both: error[E0308]: `if` and `else` have incompatible types --> file.rs:10:9 | 7 | let x = if true { | _____________- 8 | | A | | - expected because of this 9 | | } else { 10 | | B | | ^ expected struct `A`, found struct `B` 11 | | }; | |_____- `if` and `else` have incompatible types This could be done, but that has the potential to give you a lot of output for all possible traits that could satisfy this code, and in general we would be sending you in the wrong direction. When we can't be 90% sure that the suggestion is not harmful, we just "say nothing", like in this case. On a real case of the example above, you'd be more likely to want to create a new enum.
- athrowaway3z 6y agoBoxing isn't necessary. It's just not very pretty. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ed142fd586aab5a4f7cf1fee290616b9 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- Twisol 6y agoThat snippet gives a warning: "trait objects without an explicit `dyn` are deprecated". Adding the `dyn` in the right place (`&mut dyn Iterator<Item=i32>`) makes it a little more clear that you're still paying the costs of a fat pointer (half for the trait object pointer, half for the instance pointer), even if the instance is indeed stack-allocated and not heap-allocated ("Box"). If you're returning the `dyn Iterator` from this function, you'd likely need to Box it anyway, since it will go out of scope on return. (Of course, you inlined the function to account for this ;) ) None of which is to say you're wrong; only that different solutions will be appropriate for different cases. "Box" will probably work more consistently in more cases, but it's definitely valuable to have the stack-allocated approach when it's applicable.
- justinsaccount 6y agoyeah.. that's basically what i was trying to do: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=abfdb291bc999ac66f9117784a082722 https://play.rust-lang.org/?version=stable&mode=debug&editio... so where it says = note: expected type `Rev<std::vec::IntoIter<_>>` found struct `std::vec::IntoIter<_>` it could not be more unhelpful. I know those things are different, however the following "for i in output" works for both of those things individually, so why does it matter that the types are different since they both implement iteration?
- Twisol 6y agoI think the key idea here is that Rust consistently uses static dispatch by default. When you invoke some method defined by a trait, it needs to look up at compile-time which actual implementation it should dispatch to. Since the if-else expression isn't returning the same type, it doesn't matter if they both implement Iterator -- Rust still doesn't know which actual type will be produced, so it won't be able to tell which method implementations should actually be dispatched to. Dynamic dispatch, which solves the issue you faced, needs to be explicitly opted into using the `dyn Trait` syntax, since it introduces a hidden indirection through a fat pointer. This is definitely a difference from languages like Java or Python, where dynamic dispatch is the default (and sometimes there's no way to explicitly use static dispatch). On the other hand, languages like C and C++ also use static dispatch by default, with mechanisms like function pointers or `virtual` to provide dynamic dispatch as needed. You would very likely have faced a similar problem in C++, had you used `auto x = (..) ? ... : ...`; (If you used `T x = ...; if (...) { x = ... } ...`, you'd have been faced immediately with the issue of "what type should T be" anyway, I think.)