Add map! and set! macros to std

It's at least both IMO. Certainly nowhere near disingenuous. For many folks, left-pad serves as an indicator for when dependencies get out of control. That can be true while it being simultaneously true that left-pad broke the ecosystem because it was allowed to be removed in the first place.

(I personally try to be more direct and just say "proliferation of micro crates," which I think is pretty clearly what is meant here.)

8 Likes

There were two good reasons to have the vec! macro:

  • It mirrors the syntax for arrays. Where one would write [a; n], one could instead write vec![a; n] and get a vector. This is also a simple workaround for most cases where a is Clone but not Copy. Similarly, one can turn a static array [a, b, c] into a dynamic array just by prefixing it with vec!, i.e. vec![a, b, c].

    From a learning perspective, this draws a nice parallel between static and dynamic arrays. The syntax is almost the same, which also means it's easy to switch between fixed-size static and variable-size dynamic allocation whenever one changes their mind.

  • Until a few years ago, arrays were not IntoIterator. This means that the simplest way to iterate by-value over a given sequence was to construct a vector. Code like

    for x in vec![a, b, c] { .. }
    

    was all over the place. Inefficient? Maybe, but it's simple, and the allocation likely won't matter. Similarly, vec![..] would be passed around whenever one needed to pass IntoIterator<Item=T>.

    Now that arrays are iterable, this case no longer applies, but it was important for a long time.

None of these reasons apply to map literals, so I'd say the case for them is very weak. It's not like people construct raw map literals all over the place, like with vec![..] iterators. The case is very niche. Many cases which would be served by map literals in other languages, like JSON objects, use their own custom mapping types and their own macros.

So the win is basically a few less characters to type (O(1)), in turn complicating the api of std and possibly introducing ambiguity (if the same map! macro is supposed to create several types of maps). I'd say it's clearly not worth it.

1 Like

Adding a map macro clearly will not complicate the api, It's adding a very basic and self explanatory functionality.

As for the ambiguity around map! I agree, It should be hmap and bmap (and hset and bset) this way we get more functionality and less ambiguity.

6 Likes

No, the win is more readable code. The few extra characters also come with very undesirable rustfmt formatting making the code needlessly hard to read.

7 Likes

What rustfmt does isn't a law of the universe, especially since getting rustfmt to format the macro nicely would also presumably be a code change to rustfmt.

If everyone says that the way rustfmt handles pairs is poor, that sounds like a good bug to file to fix in a future formatting edition. I know they're talking about one right now.

5 Likes

I think the way rustfmt handles pairs is fine normally, large literals of [(K, V)] are uncommon except when being used with maps.

Adding macros also allows the more distinctive notation of key => value, like the hashmap_macro crate:

let m: HashMap<&str, u32> = hashmap! {
    "a" => 1,
    "b" => 2,
};

+1 to copy-pasting the maplit crate into std::collections. This is one of those crates that I import fairly frequently since it just makes code clearer and nicer. In general, any crate which extends the standard library types with small, uncontroversially-designed features should be strongly considered for merging into std. Don't make us reach for third-party libs for trivial stuff like this.

9 Likes

With overflow_delimited_expr = true in rustfmt.toml, I guess this formatting instead:

HashMap::from_iter([
    (dimension::Kind::Overworld, Dimension {
        persistent: vec![PersistentArea::Square { .. }],
    }),
    (dimension::Kind::Nether, Dimension {
        persistent: vec![PersistentArea::Square { .. }],
    }),
]);

I believe the consensus is to make that the default behavior in edition 2024, so does that resolve the biggest issue here?

6 Likes

We just need to make a => b another way to spell the 2-tuple constructor (a, b) and we're done :wink:

Scala actually does it like that via its prelude adding an extension method named -> to all types…

5 Likes

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