Repository navigation
C++-style initializer list #1602
Description
Activity
I'd prefer
hash_map!([i32, i32], 1 => 2, 3 => 4) hash_map!([i32, i32], 1 => 2, 3 => 4)
Reacted by Sebastian MaltonReacted by Julien H, ActuallyaDeviloper, hh9527, Eliza Weisman, Isaac Woods, SasakiSaki, pzxmsry and Islam Ismailov@KalitaAlexey what I suggest is that we introduce less sugaring, this way the syntax will be more powerful.
For example, my proposal is not about pair, it just happened to involve
(U, V), because the list item is(U, V).This
(U, V)can be something else, like plainT.
This way, after we can drain array, maybe after we introduce type-level integer, we can removevec!macro entirely.let v: Vec<i32> = <Vec<_>> { 1, 2 };
will be desugared to
let v: Vec<i32> = [1, 2].drain_to_iter().collect();
Reacted by Isaac Woods@bombless When we introduce type-level integer generics we could make impl
impl<N, T> From<[T; N]> for Vec<T>
And use it in the following form:
let v: Vec<i32> = [1, 2].into();
I am against this syntax because it adds complexity to parser, developers without any significant profit.
Reacted by Bene@KalitaAlexey
I'm not sure buthash_map!([i32, i32], 1 => 2, 3 => 4)
looks very ugly.
I will try to implement my syntax.
@bombless Uglier than:
<HashMap<i32, i32>> { // wrapping type name with <> to indicate type like in UFCS ( 1, 2 ), ( 3, 4 ), };
?
@KalitaAlexey
Well I didn't know you don't like my proposed syntax.As I mentioned above, initializer list is very powerful, otherwise C++ community won't accept it.
Reacted by Julien H and Tino@bombless I don't like C++. I am working as C++ programmer, but C++ is ugly. We shouldn't accept any feature from C++ just because it is from C++. Let's wait for another people opinions.
Reacted by Roland Steiner, oxalica and BeneReacted by Yupei Liu, Abhinav Gauniyal, Tino, Smoothstep, Hennadii Chernyshchyk, pzxmsry and iluha168Reacted by Spencer Tupis, Eliza Weisman, Jesse Wright, Bene and Houssam ElbadissiPlus you don't add one macro for each type, that looks insane.
@bombless Why not? Why does it look insane?
@KalitaAlexey Macros exist only because type system is not powerful enough.
Let's build a type-riched language and don't look back.Macros exist because it allows many things without extending syntax for each case. Rust will end like C++ with many-many features, but with ugly-ugly syntax.
And about type system.
With type-level integer generics we could writeimpl<K, V, N> From<[(K, V); N]> for HashMap<K, V>
And use:
let m: HashMap<i32, i32> = [(1, 2), (3, 4)].into();
Without extra syntax.
Reacted by BeneLike I said,
Now you might argue that if the desugared form is already okay why should we accept this fancy sugar.
I say this sugaring form is much more human-parsable and can make life easier.You see,
let m: HashMap<i32, i32> = vec![(1, 2), (3, 4)].into_iter().collect();
already looks okay, so I'd suggest macro is off-topic here.
I think @KalitaAlexey's integer-generics idea looks pretty nice, and it may be worth just waiting until then.
My main issue with that approach is that it seems to involve pushing the entire array onto the stack and then moving each element into the map individually, which in extreme cases could lead to stack overflow if the optimizer isn't clever enough to figure that out.
How do C++ initializer lists utilize stack space?
@rphmeier Same. There is no way to utilize any more efficient.
According to http://www.cplusplus.com/reference/initializer_list/initializer_list3 remaining items
I agree; this syntax is ugly. It should at least use type ascription instead of putting the type name in anglebrackets, and even then, I think we're better off with #542.
👎 to extra syntax. There doesn't seem to be enough to pull it's weight. A macro would make sense, however.
You see,
let m: HashMap<i32, i32> = vec![(1, 2), (3, 4)].into_iter().collect();
already looks okay, so I'd suggest macro is off-topic here.
The point isn't the syntax, it is insertion at compile time, to improve runtime performance.
Plus you don't add one macro for each type, that looks insane.
The type system want to talk to you.
The point isn't the syntax, it is insertion at compile time, to improve runtime performance.
One can't insert into a
HashMapat compile time: for the default hasher, the keys required are generated randomly at runtime, and a runtime allocation is required. Anyway, calling.insertshould be able to do a pile of optimisation already (e.g. it can be inlined and/or evaluated at compile time when the arguments are known statically).There's two reasons I would think special syntax might be faster than calling
insertrepeatedly or.collect:- preallocating an exact size (possible with
HashMap::with_capacity()and.collectdoes a best-effort preallocation, that works fine for iterators likeVec's) - constructing values in-place inside the hashmap, rather than copying onto the stack and then into the map (this would require defining the syntax to work like that, and will be manually possible soon: Tracking issue for placement new rust#27779).
- preallocating an exact size (possible with
One can't insert into a HashMap at compile time: for the default hasher, the keys required are generated randomly at runtime, and a runtime allocation is required.
Of course, but if the value is known at compile time it is very much possible to hash it at compile time. Even though, my points were mainly the two you listed afterwards.
- addedT-langRelevant to the language team, which will review and decide on the RFC.Relevant to the language team, which will review and decide on the RFC.
on Aug 18, 2016 This is really a job for a macro (like
vec!).Reacted by nicole mazzuca, est31, Eliza Weisman, leonardo-m, Jesse Wright, Hutch and Houssam ElbadissiHere is a valid macro:
macro_rules! map { ($($key:expr => $val:expr),*) => (vec![$(($key, $val)),*].into_iter().collect()) }
Just to be clear,
initializer_listis one of the most hated features of C++11.Reacted by OvermindDL1, Nick Dugger, Smoothstep, Smit, Bohdan Kurylovych, P4Cu and NewDawn0(We should add variadics, basically)
Reacted by Andrew Lilley Brinker, Ixrec, delacian and OvermindDL1@ubsan I'll have to strongly disagree with "most hated" since the benefits of
initializer_listare among the biggest wins of C++11 for me in day-to-day programming, but I'd agree that it's a massive backwards-compatibility hack that should not be blindly copied, especially since Rust can probably get all the same benefits without the ugly corner cases by doing proper variadics instead.Reacted by Isaac Woods@Ixrec maybe only among my friends, then - it's so bad for two reasons. First of all, uniform initialization means something different than parenthesized initialization, and no standard library functions use uniform initialization. Second, it provides a const view of the data, which is awful for non-trivially-copyable types - basically, you're forced to copy every single value. Don't use uniform initialization for
std::vector<std::string>! It should never have been put into C++11 - variadics should have been used.Closing in favor of #542.
In C++ you can use an initializer to initialize a map, which looks like a map literal and feels nice.
playground
So I suggest we can do something like this:
which do similar thing as above C++ code.
We already have initializer list built-in so we don't need to tackle about the initializer side.
Let me explain how above Rust code work:
It is desugared to:
playground
The trick is that
impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>is done already.To sum up, points are:
Now you might argue that if the desugared form is already okay why should we accept this fancy sugar.
I say this sugaring form is much more human-parsable and can make life easier.