Literal applied to a FromIterator type

There exist simple utilities such as hash_set![], hash_map!{} and so on, not present in the standard library. I got the following ideas for the Rust language and its standard library:

  • A set! macro which supports a FromIterator of V entries. It needs to be typed since there's no default ideal type.
  • A map! macro which supports a FromIterator of (K, V) entries. It needs to be typed since there's no default ideal type.
struct S {
    m: HashMap<&'static str, &'static str>,
}

let _ = S {
    m: map! { "k" => "v" })
};

fn take_map(map: HashMap<&'static str, &'static str>) {
}

Rest

Rest syntax is not necessary.

Example Implementation

pub macro map {
    ($($key:expr => $value:expr,)+) => {
        {
            ::std::iter::FromIterator::from_iter([$(($key, $value)),+])
        }
    },
    ($($key:expr => $value:expr),*) => {
        {
            ::std::iter::FromIterator::from_iter([$(($key, $value)),+])
        }
    }
}

Compatibility

This will affect the prelude by adding two macros similiar to vec!: map! and set!.

Deleted

I honestly can not recall a single time I needed a hash literal.

I literally wrote one yesterday in a testcase

assert_eq!(
    Config::from_str(...)?,
    Config {
        ...
        dimensions: HashMap::from_iter([(
            dimension::Kind::Overworld,
            Dimension {
                ...
            }
        ),]),
    },
);

Literal syntax (whether builtin or macro) would make this look a little nicer, rustfmt doesn't do a great job with an array of tuples

        dimensions: map![
            dimension::Kind::Overworld => Dimension {
                ...
            }
        ],

(you should be able to do something similar in the macro expansion using an array as the iterator source instead of a vec).

1 Like

Right, simplified.

For comparison, Swift’s ArrayLiteralConvertible is essentially the first part of this, though the run-time implementation is suboptimal for a handful of reasons. Swift also has a dedicated DictionaryLiteralConvertible, but map! probably makes more sense for Rust.

1 Like

Note that HashMap<K, V> implements From<[(K, V); _]> so the following fairly concise forms are already possible:

let hashmap = HashMap::from([
   ("a", "foo"),
   ("b", "bar"),
]);

as well as

let hashmap: HashMap<_, _> = [
   ("a", "foo"),
   ("b", "bar"),
].into();

Also remember that typically from_iter is spelled collect:

let hashmap: HashMap<_, _> = ["afoo", "bbar"]
    .into_iter()
    .map(|s| (&s[..1], &s[1..]))
    .collect();
9 Likes

That's true, but also note that calling from_iter directly might be more concise and readable in the case where you need to specify the type and don't have a let to hang it on, or want it to look like T::new() and T::from() for consistency. FromIterator is even in the prelude since edition 2021, so it doesn't need to be imported to be used explicitly. So, Foo::from_iter([elem, elem, ...]) will work for every collection that you could also collect() into.

5 Likes

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