Cannot understand this error: HashMap::Keys::rev()

I am trying to ger a reversed iterator

    palette:HashMap<usize, Rgb<u8>>,

// Using it
for (k, _) in self.palette.keys().rev() {

:
:
}

I get the following error that I am finding completely inscrutable

the trait bound `std::collections::hash_map::Keys<'_, usize, image::Rgb<u8>>: std::iter::DoubleEndedIterator` is not satisfied

the trait `std::iter::DoubleEndedIterator` is not implemented for `std::collections::hash_map::Keys<'_, usize, image::Rgb<u8>>`

note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Rev<std::collections::hash_map::Keys<'_, usize, image::Rgb<u8>>>`
note: required because of the requirements on the impl of `std::iter::IntoIterator` for `std::iter::Rev<std::collections::hash_map::Keys<'_, usize, image::Rgb<u8>>>` [E0277]


This is where I read up on this: Keys in std::collections::hash_map - Rust

What is not comprehensible about that, in particular? The error message itself is pretty clear: HashMap's iterator is not reversible. The reason for that is that iteration over a hash map happens in random order, so it doesn't make sense to "reverse" it. The reverse of a random order is still a random order, so you don't gain anything by trying to reverse the iterator.

4 Likes

Use a BTreeMap instead if you need a reverse iterator and the keys are sorted in this collection.

1 Like

Pretty much everything.

True. I meant to sort the Keys (only can be done on nightly be the looks)

But the documentation has rev

1 Like
		    let mut keys:Vec<usize> = self.palette.iter().
			map(|k| *k.0).collect::<Vec<usize>>();
		    keys.sort();
		    
		    for k in keys.iter().rev() {

That is what I was looking for.

Seems cumborsome. I was hoping for: let keys = self.palette.iter().map(|k| *k.0).collect().sort().rev(); but sort works in place so cannot be chained

I still find the error message utterly inscrutable

There's a trait bound on that method, where Self: DoubleEndedIteratot. Keys doesn't implement that. Hence the error.

5 Likes

Complaining using strong words is not a great way to learn. A bit of humility doesn't hurt – maybe you should look for meaning in the error message and make some effort to understand it, instead of being depreciating as a first reaction.

4 Likes

As does any attempt to use the tool which is not the best for the job. If you need the keys to be ordered, it'd be better to use BTreeMap, not HashMap.

5 Likes

You're right. That's not great.

I've opened a ticket on the issue tracker for this one.

13 Likes

Opening an issue for the other problem, with the error messages, is tougher. Being told that "everything" in an error message is bad doesn't give any hints about what the compiler should say instead. It's why the code of conduct says to keep unstructured criticism to a minimum; they need more information about what's exactly wrong in order to fix it.

7 Likes

It's interesting that the error doesn't mention the rev method at all, even though it's bounds weren't satisfied either. It would probably be clearer to call that out first since its the thing that's directly causing the problem.

7 Likes

It does mention it (in the first error).

error[E0277]: the trait bound `std::collections::hash_map::Keys<'_, usize, Rgb<u8>>: DoubleEndedIterator` is not satisfied
    --> src/main.rs:6:34
     |
6    |     for (k, _) in palette.keys().rev() {
     |                                  ^^^ the trait `DoubleEndedIterator` is not implemented for `std::collections::hash_map::Keys<'_, usize, Rgb<u8>>`
     |
note: required by a bound in `rev`
8 Likes

Well that's embarrassing! I guess I was expecting the "method exists... but it's trait bounds were not satisfied" style of error for some reason.

5 Likes

Rust error messages so often put the most important part at the beginning. I tended to miss it at first, since I expected to have to dig a bit.

It is really hard to know where to begin when so hopelessly confused.

Calling rev() on the keys of a hash, clearly I was over the edge. (Documented but...)

As pointed out above the error message should say that there is no rev() for that.

It dose say that, technically, but it has buried it. But I could not understand the error message until I had figured out what was wrong. That is the wrong order.

Rereading the error message now I cannot see how it actually says "there is no rev here". I get the point it is making, now, after the fact

Well, the thing is, rev() does exist. Saying that there is no rev() would simply be both technically wrong and misleading, because people would then wonder if they made a typo, or if they are looking entirely on the wrong type, etc.

The error message tells exactly the truth; the problem lies not within the existence of the method but the constraints on it, that don't allow it to be called with particular type parameters. It's much better to point out that "this method exists but you aren't allowed to call it for other reasons", because 1. it's not a lie like asserting it doesn't exist would be, and 2. it contains/preserves information as to what the reason for the error is.

Taking that information away would make the error message much harder to understand and debug, actually. The right thing to do here is not for the compiler to resort to some sort of hand-waving, but for you to familiarize yourself with terminology around generics, such as "constraints" and "bounds".

2 Likes

We are talking past each other.

rev() is not defined on HashMap::keys(). It makes no sense that it would.

The error message tells exactly the truth

Here we are talking past each other.

The error message is technically factual, but it is no help.

the problem lies not within the existence of the method but the constraints on it

That is splitting hairs. You cannot call rev on the HashMap::keys iterator.

"this method exists but you aren't allowed to call it for other reasons",

That is a good sentence. Needs some work before it is a good error message but it conveys the point, to the point.

In a way that....

the trait bound `std::collections::hash_map::Keys<'_, usize, image::Rgb<u8>>: std::iter::DoubleEndedIterator` is not satisfied

...does not.

but for you to familiarize yourself with terminology around generics, such as "constraints" and "bounds".

Really? It is my fault? No it is not! That was unnecessary.

1 Like

I agree. Rust prides itself on trying to make diagnostic messages understandable and to the point, there should be absolutely no room for privileged lecturing from a position of power/expertise that there’s nothing wrong with an error message and that the complainer should just stop being too stupid/lazy/ignorant to understand it.

The error messages related to unsatisfied trait bounds could absolutely use work. They’re not nearly always straightforward to decipher even to an intermediate-level Rust programmer. Sometimes they’re plain misleading.

1 Like

The error message tells you the exact reason for the error as concisely as possible. The reason is that the given trait bound is not met, there is no more to it. What else do you want?

No it's not – my very point is exactly that the distinction matters a lot.

I'm sorry, but my point stands. The reasonable thing to do with a programming language is to learn it, and not to expect that you be spoon-fed every last bit of knowledge for magically correcting every error.

I have no idea what you are trying to say with "power" but I'm not any more powerful than anyone else here, and I don't see the relevance of it to this topic anyway. Learning Rust includes learning the terminology, that's all I'm trying to say. It's not possible to spare the effort of learning and getting experienced over time, that is for sure.

2 Likes

I don't think the error is wrong, but I do think in this specific instance it's kind of backwards especially if you're new to the language. The user isn't directly using the types or traits it mentions first after all.

Starting with "the method exists but it's bounds weren't satisfied" would make it much easier to understand the rest of the error if you didn't already know about the details of how the iterator protocols are organized.

Granted if you were calling rev on an iterator you wrote, then the current way it's organized might be slightly better. In that case you might actually just be missing a DoubleEndedIterator impl so it is a bit of a balancing act.

1 Like