Add cmp::min/max to prelude?

What do people feel about this? min and max are such trivial mathematical functions and I find myself including them so much, it seems a little silly that they’re not in the prelude.

Incidentally, they are currently the only items of std::cmp not in the prelude.

2 Likes

I’d like max() and min() usable like x.max(y).

1 Like

Ugly syntax. Really? Would you like to write 1.add(2) instead of 1 + 2?

min and max are trivial functions; simpler than addition and multiplication even.

I think of max/min mostly as infix functions (and in D language I use x.min(y) all the time, because of UFCS).

Take a look at the Rust standard library, there are many other methods like that (on the other hand I don't like x.atan2(y) much).

In Rust there isn't an operator to perform min/max, so I can't compare the two situations.

A common usage pattern is:

x = max(x, y); x = x.max(y);

So you can also think about implementing that with another method, but it looks a little overkill:

x.max_update(y);

I also think min and max would be good candidates for the prelude, but it would technically be a breaking change.

I don't think min/max should be methods, but do you know that you can write 2.add(2)? Rust Playground

Why? Aren't names from prelude shadowed by any other symbol?

I would be against such a change, because the prelude should be for things that are in nearly every program. Certain kinds of programs will use it often, but others won’t. I’m not sure I’ve ever used it for anything other than the Guessing Game, for example.

Everything else is because the traits are; == is extremely common. And since it returns an Ordering, that has to be as well.

You're right! Never mind about that concern.

This is a fair argument, but I think they're used very frequently to write little 'mathy' bits of code, and Python for example has them available as built ins. What do you see as the harm of a larger prelude? (I'm generally in favor of expanding it; I think it should probably include Read, Write, and the io module, for example).

You're thinking of the Ord methods, min and max return the larger or lesser of the two inputs.

(But then again, I do think Ordering should be in the prelude since the methods that return Ordering are already in the prelude, and they're not very useful without the enum.)

There's use std::io::prelude::* specifically for that purpose.

I know, I’m saying I think these three symbols should be imported by default.

I actually tend not to use the io prelude because it requires a separate statement to import io, which I usually need for io::Result and io::Error; i.e I prefer to enumerate the traits I need to keep the use statement to one line:

use std::io::{self, Read, Write};
// vs
use std::io;
use std::io::prelude::*;
2 Likes

Yup, I was looking at a different tab there. Whoops!

It's a symbol that's imported into every program. And to save a single use statement, there's not a whole lot of gain. Plus, the aforementioned "this is sort of a breaking change" issue.

Are you willing to explain why (keeping in mind the cases like x.atan2(y) of the std lib)?

Maybe I write my code a little differently, but I tend to use them quite a bit for non-numerical stuff, e.g.

        let len = min(random_length(), max_len);

or

    fn pad<W: Write>(w: &mut W, n1: usize) -> Result<()> {
        let zeros = [0u8; 16];
        let mut n = n1;
        while n > 0 {
            n -= match try!(w.write(&zeros[0..min(n, zeros.len())])) {
                0 => return make_io_err(ErrorKind::WriteZero, "write failed"),
                x => x
            };
        }
        Ok(())
    }

But ultimately, I feel prelude contents should be decided based on what a large number of people tend to use, not just one person like me.


On a slightly different point, it would be nice to be able to import symbols once for the whole project (i.e. a custom prelude for your whole project, or "global" project-wide imports).

There's no clear receiver.

It turns out its not a breaking change because prelude imports are shadowed fine. But its one use statement per module that needs them, not per crate, which adds up in my opinion.

1 Like

Sorry if I am slow, but receiver of what? I don't understand...

P.S.: But I see you want to use min() and max with other user-defined types. So the free functions are useful.

If you have to do that, you might as well be explicit and do std::io::{Read, Write} since those are the most common.

The “receiver” of a method is the value which takes self. If swapping the position of the arguments doesn’t change the function’s semantics, I don’t think it should be a method.

The operators make the lhs the receiver because they have to in order to fit in with the way Rust’s trait system works.

However, your comment about using them on user defined Ord types doesn’t matter so much; these functions could have been defined as methods on Ord with default implementations (this would also have made them in scope by default, incidentally). I don’t think this matters so much.


FWIW what really seems odd to me is that the cmp traits are in the prelude. You almost never need them. You can use their operator forms without the traits being in scope, and most of the time people implement them by derive, rather than manually. I’m much more likely to use min or max than use these traits! Ord and PartialOrd aren’t even usable without importing Ordering. It seems almost like a mistake that they are included at all.

I’m also much more likely to need to import Read, Write, Display or Debug than these traits, in my experience, but that’s a bit out of scope.

Totally agree with you, @withoutboats. I sometimes derive PartialEq but that’s about it.

Removing existing items from the prelude doesn’t make sense IMO, but what should the criterion be for adding items? Perhaps (a) that the item is commonly used (among the community, not by every developer), (b) that the name have clear meaning without extra context (not confusing or esoteric) and © that the name is not likely to conflict with another item of the same name.

This would seem logical to me, except that a metric might be useful for (a) (scanning Cargo packages maybe).

1 Like

I’m wondering why min and max are methods on f32 and f64 but not on any other type. Why is there such an inconsistency?

1 Like