* the function's argument is a tuple (this is the more common way, not only in SML, but also how it's done most commonly in maths);
* currying.
I'm not a fan of currying, because it singles out the first argument over the others. However, the first approach (tuples) is very ergonomic, because now you can pass the return value of one function as an argument to a "multi-argument" function. One application of that is function composition. But another is a simple map. I've lost count of how many times in Rust I had an "o: Option<(A, B)>" and a "f: fn(A, B) -> C" and couldn't call "o.map(f)"; instead the programmer is forced to write more noisy code with lambdas. And lambdas sometimes don't play well with the borrow checker for mysterious reasons (even those that don't capture anything).
Also, I think it would be nice if the compiler generated named discriminants for every enum. Currently, "std::mem::discriminant" returns an opaque "std::mem::Discriminant<T>" (which doesn't have a name). In order to compare discriminants I need to create a full-blown enum value and extract the discriminant from it later. I've worked around this problem by using the "strum" crate, which has a macro to produce another (plain) enum, but having the discriminant type be named "std::mem::Discriminant<T>" (to signal the connection to the original enum) and have named variants at the same time would be the best of both worlds.
EDIT: If I knew more about possible breakage caused by my first suggestion, I could probably start working on an RFC. However, I feel I don't have enough knowledge about Rust to anticipate that.
For my second suggestion I think I'm going to try to propose that. I'm reading the docs on the process now.