3 Things that the Rust Standard Library should have…

Hello,

In my humble opinion the STD should have included the following 3 things:

The macros for creating HashMap’s, Hashset’s and BTreeMap’s and BtreeSet’s from the package maplit:

maplit
Collection “literal” macros for HashMap, HashSet, BTreeMap, and BtreeSet.
https://crates.io/crates/maplit

let map = hashmap!{
"a" => 1,
"b" => 2,
};

I have made over the times, several structs for a Vec2D or a Vec3D from a normal Vec or a Array2D or a Array3D from a normal array. There should be same direct way of making multi-dimensional ways of creating and accessing (get’s and set’s) that worked like a array[i][j][k] or vec[i][j][k] and that you could have access to the bounds of each dimension. This would be a really simple time and cognition saver. Because it’s very common.

3º Graph’s. If graph’s are a data structure used everywhere (graph’s, tree’s, networks) and they need to be highly optimized, with many complex algorithms that are implemented over graphs. Last but not lest, graphs aren’t something easy to implement in Rust (because of reference cycles), why doesn’t Rust STD gives us a good Standard library implementation for graphs with many algorithms over graphs?

Those 3 things are all “low hanging fruit” is there a reason why the Standard library should not address this 3 things.

Thank you,

Best regards and have a nice day,
João

I think the best suggestion here is the macro, but a hash map can already be constructed like this, which seems just as good as your macro.

use std::collections::HashMap;

let solar_distance = HashMap::from([
    ("Mercury", 0.4),
    ("Venus", 0.7),
    ("Earth", 1.0),
    ("Mars", 1.5),
]);

I'm not too sure about what the exact details of your second suggestion is. As for the third suggestion, it actually surprises me a bit. I can't think of a single language with a graph data structure in the standard library, and there are many ways to implement graphs with different tradeoffs. Implementing a comprehensive graph library is a big task, and seems like it would be easy to make some poor API decisions. Those are generally a good indication that something should be an external library.

24 Likes

I'm basically of the same opinion as @alice, and about your second point I (personnally) don't think those kind of structures are as common as you think, they are very common in teaching material and maybe scientific programming, but when I've actually encountered a 2 dimensional problem in my work I've generally projected the problem onto 1 dimension which is simpler to handle.

1 Like

Discussions for improving the language are usually better posted to https://internals.rust-lang.org ; this forum is focused instead on helping people use current Rust.

1 Like

A quick search found petgraph. I've no idea how good it is, though.

Hello again,

@2e71828 this is not a recommendation for improving the Rust language kind of grade post, this is so simple trivia that any beginner can relate with, that’s why I called the 3 things low hanging fruit.

And the fact that I didn’t even known that there was a forum called https://internals.rust-lang.org , thanks for telling me that, it’s nice to know to post more serious internals questions when the need arises.

1º Macros

@alice and @erelde, I think that any good programming language should be playful, should have the characteristic that makes you reach for it just for the having a nice time with it because, maybe one is bored or because one simply wants to play with something. And that was what happen to me yesterday. I had some free time and I wanted to play around with graph algorithms, to remember somethings and to learn some new things in a joyful and playful spirit. Python has this playful quality and I think Rust also has this quality. I have a really nice time playing with Rust.

I started by studying and porting to Rust some graph algorithms from the following Python algorithms repository:

And then when I was on the port of the second algorithm Articulation Points I came to the following very clean Python code to implement a simple demo graph:

# Adjacency list of graph
data = {
    0: [1, 2],
    1: [0, 2],
    2: [0, 1, 3, 5],
    3: [2, 4],
    4: [3],
    5: [2, 6, 8],
    6: [5, 7],
    7: [6, 8],
    8: [5, 7],
}

And I look around for a good way to do this in Rust and have found in the Rust documentation what @alice wrote, and made this:

        // Adjacency list of graph.
        let data: GraphAP = HashMap::from([
            (0, vec![1, 2]),
            (1, vec![0, 2]),
            (2, vec![0, 1, 3, 5]),
            (3, vec![2, 4]),
            (4, vec![3]),
            (5, vec![2, 6, 8]),
            (6, vec![5, 7]),
            (7, vec![6, 8]),
            (8, vec![5, 7]),
        ]);

For Rust, it was a better way to write it then just making HashMap inserts and I was happy with it, but then I looked at it and it was not has playful has Python version. I asked to myself if there couldn’t exist a macro that would make a better jobs in a more aesthetically pleasant way, because it started to resemble my LISP and Scheme days 25 years later (for myself) hehehe

So I found maplit, and loved it’s simplicity and it’s playful characteristic that the dictionaries in Python also have, so Rust was in pair with Python in this regard, and that is really nice, but then I sow that I had to add to toml a crate and had to make that long import and that simply messes all the yin yang of the playful characteristic that I was talking about, so my question arise:

Why isn’t this macro in the STD?

With the from:

// Adjacency list of graph.
let data: GraphAP = HashMap::from([
    (0, vec![1, 2]),
    (1, vec![0, 2]),
    (2, vec![0, 1, 3, 5]),
    (3, vec![2, 4]),
    (4, vec![3]),
    (5, vec![2, 6, 8]),
    (6, vec![5, 7]),
    (7, vec![6, 8]),
    (8, vec![5, 7]),
]);

With the macro:

#[macro_use] extern crate maplit;        

let map = hashmap!{
    0 => vec![1, 2],
    1 => vec![0, 2],
    2 => vec![0, 1, 3, 5],
    3 => vec![2, 4],
    4 => vec![3],
    5 => vec![2, 6, 8],
    6 => vec![5, 7],
    7 => vec![6, 8],
    8 => vec![5, 7]
};

2º Multi-dimensional Arrays ans Vec’s.

@erelde, you explained that it isn’t very common the 2D and 3D arrays (stack) and Vec (heap), well as you also said it’s common in scientific computing(things like NumPy prove it) and in learning, and it is also a playful characteristic that I think Rust could have. Easy in, do something in your free time just for pleasure (in algorithms, in science or engineering, in mathematics or in physics) and easy out, it would remove some friction, some boiler plate. It would be very nice :slight_smile: Just my 2 cents!

3º Graph’s

I have to follow @alice recommendation and learn more about:

petgraph
Graph data structure library. Provides graph types and graph algorithms.
https://crates.io/crates/petgraph

But I really would like to see Graph’s to have a place in the STD because they area common, if you thing of graphs as graphs, tree’s and networks, that they are in reality the some thing under the hood.

Thank you,

Best regards and have a nice day,
João

In that case, I must have misunderstood your post— I read it as advocating for a change to std. What information are you seeking here?

2 Likes

When I think about internals foruns of a programming language, I think of institutional things, it makes me think of standard committees of languages like C++ and that kind of stuff.
This post is just me a simple person talking about 3 simple things not an institutional thing (grade post of a change proposal) that could be “read it as advocating for a change to STD”, kind of post. Is just me talking out loud what others could also think about, nothing more then that.

Best regards,
João

The HashMap::from([...]) thing is relatively new (it appeared in rust 1.56). So maybe there will be a macro to improve the syntax even more sometime soon? On the other hand, maybe that generic "create a map" macro would not give that much benefit anyway, compared to a more specific macro, that could be a part of some used crate, or that you could implement yourself.

E.g. something like this:

    let data2 = graph![
        0 => 1, 2;
        1 => 0, 2;
        2 => 0, 1, 3, 5;
        3 => 2, 4;
        4 => 3;
        5 => 2, 6, 8;
        6 => 5, 7;
        7 => 6, 8;
        8 => 5, 7
    ];

... is not that hard to create with a macro of your own (playground link) (In my example it just creates a hashmap, in a graph crate it would probably create a graph type that may use a hashmap (or a BTreeMap) for its implementation).

3 Likes

Hello,

@kaj I didn’t knew that the HashMap::from() was so new, and if a macro appears that will be great, but I really liked your idea and syntax for a macro for graph’s. The big leap that your post has given me is your example code, explaining clearly that is really simple to make a Macro to transform your nice syntax (even better then the Python one for this case) into a HashMap::from().

In the past I have in more then one occasion study Macros, but lack the practice of making Macros. So for this I was always thinking that a Macro would be more difficult to implement then your example have shown to be.

Your solution is a really nice one.
Thanks a lot @kaj!

Best regards,
João

Regarding yet the previous Macro and just for future memory and for others that could have the same problem.

If you use the @kaj Macro in the same file of the macro you can just do, like he did:

use std::collections::HashMap;

macro_rules! graph {
    [$($k:expr => $($v:expr),*);*] => {
        HashMap::from([
            $(($k, vec![ $($v),* ])),*
        ])
    }
}

let data2 = graph![
        0 => 1, 2;
        1 => 0, 2
];

but if you aren’t very versed in Macro implementations and access rules of Macros (like myself) and you have to put the Macro inside a file (utils.rs) and use it in other file (file_b.rs) and have a main.rs you need to do the following:

In utils.rs: Define the macro with the complete path do the HaspMap and export it as a pub in the crate.

macro_rules! graph {
    [$($k:expr => $($v:expr),*);*] => {
        ::std::collections::HashMap::from([
            $(($k, vec![ $($v),* ])),*
        ])
    }
}

pub(crate) use graph;

In main.rs: Declare that you are using all the macros from the module / file utils.rs.

// Macros
#[macro_use] mod utils;

and in file_b.rs: where you will be using it, you have to import it and use it like this.

use crate::utils::graph;

let data2 = graph![
        0 => 1, 2;
        1 => 0, 2
];

Thank you all.

Best regards,
João

1 Like

I can't think of a single language with a graph data structure in the standard library, and there are many ways to implement graphs with different tradeoffs. Implementing a comprehensive graph library is a big task, and seems like it would be easy to make some poor API decisions.

Ladies, gentlemen, std::person<_>, let me introduce you to Erlang standard library:

  • digraph - this module provides a version of labeled directed graphs ("digraphs").
  • digraph_utils - algorithms for directed graphs.

Perhaps not very elaborated, but quite useful.

3 Likes

Are you by any chance currently doing the Advent of Code? Your points 2 and 3 are not -- in my opinion -- that general an issue, but I do encounter your three points on a daily basis doing AoC.

A bit more generally, your issue seems to be that of a beginner. It is very important that the language be friendly to beginners, and to smooth as much as possible the steep learning curve of Rust. However, I don't believe that adding more things to std is the way to go. Depending on external crates is a basis of a Rust workflow, and I think we should more guide new-commers towards tools such as cargo add and lower the barrier to their exploration of the crate ecosystem rather than shield them from pulling dependencies by putting more things in std.

Debatting the choices made in the std is a weekly occurrence on this forum, so I'm sure it's been better said before, but putting graphs are typically not something I'd like to see put in std. They can be used in a bazillion ways, with different APIs offering different trade-offs. There is likely no one-size-fits-all solution, and hence they should not be put in std. Interestingly, you gave Python as an example, but Python is plagued by things put in std which are better done by external packages or new modules of the std.

1 Like

Hello again,

I’m not doing AoC, but like you said AoC is a good example of that need to have memorable and easy ways, with low friction to go in, do your code, have a nice time doing it and go out. It’s that playfulness that I was talking about. And I’m not a beginner to development, far from it, I have many decades of it.
If you see from a certain perspective the HashMap::from([…]) was introduced already in that direction and I suppose for the same reason to lessen the friction. For practical reasons.
The macro only goes a step further and makes for a even better syntax for HashMap’s.

The second point, multi-dimensional array’s and vec’s support, or same kind of easing it in the STD, C has multidimensional array support in the language, if I remember correctly even Fortran has it, I cannot see why this isn’t implemented in all languages, it a trivial thing and it’s a nice thing. Because this was a decision in the original definition of the Rust language it can only be added some support in the types in the standard library.

The third one, it’s just a belief that I have, that because tree’s, directed graph’s and networks are all graph’s and they are common, and generic graphs aren’t trivially implemented in Rust (because of cycles) it should be that there is some kind of support in the STD for graph’s.

Best regards,
João

It is new, but FromIterator<(K, V)> has been supported since before 1.0. It doesn't look all that much different, TBH:

let map = HashMap::<_, _, RandomState>::from_iter([
    (0, vec![1, 2]),
    (1, vec![0, 2]),
    (2, vec![0, 1, 3, 5]),
    (3, vec![2, 4]),
    (4, vec![3]),
    (5, vec![2, 6, 8]),
    (6, vec![5, 7]),
    (7, vec![6, 8]),
    (8, vec![5, 7]),
]);

There are some unnecessary details going on in there, which makes From<[(K, V); N]> more easily digestible.

1 Like

I've dealt with graphs fairly frequently in my hobby coding, but have yet to encounter a situation where a graph library seemed like it would need useful. The problem is that e whenever I've got a graph (e.g. a dependency tree) I've also got a lot of associated data, and frequently multiple kinds of edges and multiple types of nodes. It's possible that there exists some super-generic graph library that could manage all this, but I'm not sure that's sixty a beast would be an improvement over coding it myself. In fact a glance again at the documentation of petgraph reinforces my aversion to it. It's simultaneously too complicated and not powerful enough.

But that may be more of a statements of my own interests than anything else.

2 Likes

Note that you're using the IntoIterator implementation for arrays there, which is also pretty new. Even in 1.50.0 it no longer works: https://rust.godbolt.org/z/ereG1coKe

Of course it works fine with vec! instead of the array, and the one extra allocation isn't material given the 10 that are already there, but that's even less tidy.

EDIT: But a new macro_rules for collection literals would also only be usable in new rust versions, so it wouldn't help. Whereas one huge advantage of crates is that they can (often) be used on older rust versions and without waiting the minimum of 6-12 weeks for new standard library things.

2 Likes

I don't think C has Vec2D/Vec3D/Array2D/Array3D built into the language. And if you're talking about things like int[2][3][4]/int[][3][4] then Rust has [[[i32; 4]; 3]; 2]/&[[[i32; 4]; 3]]

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.