Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++-style initializer list #1602

Closed
bombless opened this issue May 1, 2016 · 31 comments
Closed

C++-style initializer list #1602

bombless opened this issue May 1, 2016 · 31 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@bombless
Copy link

bombless commented May 1, 2016

In C++ you can use an initializer to initialize a map, which looks like a map literal and feels nice.

#include <map>
int main() {
    auto m = std::map<int, int> { { 1, 2 }, { 3, 4 } };
}

playground

So I suggest we can do something like this:

let m = <HashMap<i32, i32>> { // wrapping type name with <> to indicate type like in UFCS
  ( 1, 2 ),
  ( 3, 4 ),
};

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:

let m: HashMap<i32, i32> = vec![(1, 2), (3, 4)].into_iter().collect(); // I hope we can drain array here

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:

  • start with a type here so we know it's about a type, not for the propose of mentioning some associated item and not to start struct literals and such
  • wrap a list in braces and separate with comma
  • each item in the list is plain expression, so the syntax is very flexible

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.

@KalitaAlexey
Copy link

I'd prefer

hash_map!([i32, i32], 1 => 2, 3 => 4)

hash_map!([i32, i32],
          1 => 2,
          3 => 4)

@bombless
Copy link
Author

bombless commented May 1, 2016

@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 plain T.
This way, after we can drain array, maybe after we introduce type-level integer, we can remove vec! macro entirely.

let v: Vec<i32> = <Vec<_>> { 1, 2 };

will be desugared to

let v: Vec<i32> = [1, 2].drain_to_iter().collect();

@KalitaAlexey
Copy link

@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.

@bombless
Copy link
Author

bombless commented May 1, 2016

@KalitaAlexey
I'm not sure but

hash_map!([i32, i32],
          1 => 2,
          3 => 4)

looks very ugly.

I will try to implement my syntax.

@KalitaAlexey
Copy link

@bombless Uglier than:

<HashMap<i32, i32>> { // wrapping type name with <> to indicate type like in UFCS
  ( 1, 2 ),
  ( 3, 4 ),
};

?

@bombless
Copy link
Author

bombless commented May 1, 2016

@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.

@KalitaAlexey
Copy link

@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.

@bombless
Copy link
Author

bombless commented May 1, 2016

Plus you don't add one macro for each type, that looks insane.

@KalitaAlexey
Copy link

KalitaAlexey commented May 1, 2016

@bombless Why not? Why does it look insane?

@bombless
Copy link
Author

bombless commented May 1, 2016

@KalitaAlexey Macros exist only because type system is not powerful enough.
Let's build a type-riched language and don't look back.

@KalitaAlexey
Copy link

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.

@KalitaAlexey
Copy link

KalitaAlexey commented May 1, 2016

And about type system.
With type-level integer generics we could write

impl<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.

@bombless
Copy link
Author

bombless commented May 1, 2016

Like 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.

@rphmeier
Copy link

rphmeier commented May 1, 2016

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?

@KalitaAlexey
Copy link

KalitaAlexey commented May 1, 2016

@rphmeier Same. There is no way to utilize any more efficient.
According to http://www.cplusplus.com/reference/initializer_list/initializer_list

@bombless
Copy link
Author

bombless commented May 1, 2016

I see, type-level integer deserve a RFC.

@KalitaAlexey
Copy link

@bombless It already has. #1038

@alilleybrinker
Copy link

Absolutely agree about preferring type-level integers. I am strongly against additional Rust syntax without a very good motivation, and this motivation doesn't seem strong enough.

@BlacklightShining
Copy link

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.

@ticki
Copy link
Contributor

ticki commented May 2, 2016

👎 to extra syntax. There doesn't seem to be enough to pull it's weight. A macro would make sense, however.

@ticki
Copy link
Contributor

ticki commented May 2, 2016

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.

@ticki
Copy link
Contributor

ticki commented May 2, 2016

Plus you don't add one macro for each type, that looks insane.

The type system want to talk to you.

@huonw
Copy link
Member

huonw commented May 2, 2016

The point isn't the syntax, it is insertion at compile time, to improve runtime performance.

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. Anyway, calling .insert should 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 insert repeatedly or .collect:

  • preallocating an exact size (possible with HashMap::with_capacity() and .collect does a best-effort preallocation, that works fine for iterators like Vec'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).

@ticki
Copy link
Contributor

ticki commented May 2, 2016

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.

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 18, 2016
@nrc
Copy link
Member

nrc commented Aug 18, 2016

This is really a job for a macro (like vec!).

@Nokel81
Copy link
Contributor

Nokel81 commented Jun 27, 2017

Here is a valid macro:

macro_rules! map {
   ($($key:expr => $val:expr),*) => (vec![$(($key, $val)),*].into_iter().collect())
}

playground

@strega-nil
Copy link

Just to be clear, initializer_list is one of the most hated features of C++11.

@strega-nil
Copy link

(We should add variadics, basically)

@Ixrec
Copy link
Contributor

Ixrec commented Jun 28, 2017

@ubsan I'll have to strongly disagree with "most hated" since the benefits of initializer_list are 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.

@strega-nil
Copy link

@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.

@Centril
Copy link
Contributor

Centril commented Oct 7, 2018

Closing in favor of #542.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests