Should HashMap be added to std prelude?

Continuing the discussion from Should the Future trait be part of the prelude:

  • Yes
  • No

0 voters

The PR to add HashMap to std's prelude: https://github.com/rust-lang/rust/pull/63418

1 Like

On one hand, it seems strange that Vec is in the prelude and HashMap isn't. On the other hand, it has been said before that one difference between the two is that there are multiple good ways to implement a HashMap, depending on the use case.

6 Likes

I think there are more good ways to implement a HashMap than a Vec but you can still make design decisions about the latter in terms of e.g. the multiplying factor to use when reallocating.

2 Likes

I think yes, and that BtreeMap ought to be in there too.

2 Likes

If it were added to the prelude, putting a hashmap! literal macro like the one in maplit in std would make sense for parity with Vec/vec!.

14 Likes

I strongly agree with this, and think it would be bad to give HashMap special status which would lead to people picking it over BTreeMap without due consideration:

https://twitter.com/bascule/status/1156720845152108544

5 Likes

BTreeMap, on the other hand, is so complex that I haven't seen (and don't expect) many others even try to write an alternative implementation..

I would actually suggest a more general-purpose Map type to be in the prelude which is optimized by default for a small number of keys (say, a dozen). Erlang did this, through for reasons that may not apply to Rust. I would suspect the winner in std for small number of keys would be BTreeMap, but we'd want to benchmark that.

5 Likes

Can someone who knows macros better tell me if this is a workable idea or not:

Would it be possible to make a macro that builds an associative array trait object (which could default to Hashmap as the implementation type) which works like the following:

let d1 = dict!("key"=>"Value");   // type is trait object AssociativeArray
let d2 = dict!(std::collections::HashMap,"key"=>"Value"); // type is std::collections::HashMap
let d3:std::collections::BTreeMap<_,_> = dict!("key" => "Value"); // type is std::collections::BTreeMap
let d4 = dict!(d1,"key"=>"NewValue"); // takes ownership of d1, inserts keys into AssociativeArray

Maybe the skip the last one. I'm not sure.

This suggestion would preclude the introduction of a such a trait, which, given its absence, maybe there's a good reason for not having and therefore this entire suggestion is moot?

edit: Not sure why I was thinking trait objects, I think the first return could just be impl AssociativeArray

1 Like

If we have the maps, we should have the sets, too.

6 Likes

One way to gain more objective insight on this would be to analyze the crater collection of source code (IIRC someone already made something like this available?) and search for all usages of std::.

2 Likes

I agree that HashSet belongs in the prelude too.

2 Likes

Both of the standard map types implement FromIterator<(K, V)> (HashMap, BTreeMap). So for dictionary literals up to 32 items long, you can use [($key, $value),*].into_iter().collect(). For the insertion, Extend<(K, V)> (map.extend(iter::once(($key, $value)))), which is basically collect_to.

So yes, the macro would be practical, so long as you always provide the concrete type somehow. I'm pretty sure someone's written a collect! macro that does this.

Collection traits have been discussed many times (link is arbitrary first urlo result), but are blocked on at least GAT. What said traits would actually guarantee and how they'd be organized, however, is contentious. E.g. what performance suggestions do you put on what traits? But that's not this thread's discussion.

There's no true array iterator yet -- this is dereferencing into a slice and a slice::Iter by reference.

(or maybe it hits IntoIterator for &[T; N] for lengths up to 32, but that's still a slice::Iter.)

Ah, whoops, missed the &'a when checking the docs. It could be constructed with a bunch of .chain(once(($key, $val))), but that gives up the exact size knowledge unfortunately.

Agreed, e.g. I absolutely love BTreeMap when implementing recursive structures that need to be Hash or Ord; you can't do that with a HashMap because of its very (randomized) nature.

@CAD97

Just one comment for this: it is completely true. I've been working a lot in Swift, which has collection traits in std. They are a massive pain in the neck. I feel that in an attempt to generalize over every possible use-case, the Swift collections library has been massively overengineered in terms of traits. I literally didn't find a single good use case for them but they require so much wandering around the documentation (as they are implemented as ravioli code, with dozens of small traits organized into equally numerous levels of protocol conformance), it's very annoying, and I'm actually glad Rust doesn't have them.

(Meanwhile, usually I'm all for generic programming, and I actually don't find Swift's generic capabilities powerful enough, so I'm not the "you ain't need traits" guy on the block. It's just that collection traits are IMO very hard to get right, and not nearly as useful as one's intuition or inner good-practice-o-meter might say at first thought.)

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.