rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

Support use Trait::method and/or Type::method?

Open Rufflewind opened this issue 8 years ago • 4 comments

Currently Rust doesn’t support any of the following:

// Example A
use Default::default;
use f64::sin;

This means one can never write default() or sin(3.14) without qualification as individual methods can never be imported in isolation. (Of course, one can also write 3.14f64.sin(), but not everyone is a fan of reverse Polish notation; this could be a deterrent for numerical users of the language.)

One workaround is to create a prelude-like module with wrappers:

// Example B
pub fn default<T: Default>() -> T { T::default() }
pub fn sin(x: f64) -> f64 { f64::sin(x) }

This is a lot of boilerplate. Perhaps it would useful to make Example A “just work”?

For static methods, the intent is pretty clear: it would enable

static_method(arg1, arg2, …)

This would be just a shorthand for calling Trait::static_method. It won’t work if Self is ambiguous.

For non-static methods, it’s not obvious what use Trait::method would do. Which among these would it enable? (Either or both?)

method(obj, arg1, arg2, …)
obj.method(arg1, arg2, …)

@petrochenkov mentioned:

Adding use Trait::AssocItem; should be simple, it mostly needs motivation and decision. use Type::AssocItem; is not possible with current organization of compilation stages.

This issue was motivated by @HadrienG2’s Experience porting a simple MC simulation to Rust.

(The issue was moved from https://github.com/rust-lang/rust/issues/41453.)

Rufflewind avatar May 08 '17 02:05 Rufflewind

Thanks for opening this issue! Since I went through this little porting exercise, I've had a bunch of language/lib ideas like this written somewhere on a TODO list, but haven't allocated the time for opening appropriate issues and PRs so far.

Regarding the reverse polish notation part, I would like to clarify that what is most irksome for a non-native Rust speaker is the fact that idiomatic Rust mathematical expressions currently often need to mix and match two styles of math notations.

On one hand, we have the "functional style", commonly used by calculators, and which is by far the most widespread in other programming languages. It reads from left to right, with binary mathematical operators behaving in the usual fashion:

x = builtin_function(a * b + user_function(c/d));

On the other hand, we have the "method style", commonly used by object-oriented ayatollahs or Java programmers who do not have a choice. It feels less natural to write to most people, and is somewhat harder to read as the eye must move back and forth from left to right quite a bit more, but it plays nicely with OO and is consistent in its own way.

// Java-style
x = a.mul(b).plus((c/d).user_function()).builtin_function()

// C#-style (hurray, operator overloading!)
x = (a * b + (c/d).user_function()).builtin_function()

What is most tragic about Rust, however, is that since it is geared towards a functional programming style but implements basic mathematical operators as methods of the numerical types, people will need to mix both numerical type methods and free functions in a typical expression. So we end up with an unpleasant mixture of both styles which ultimately ends up being even less readable than either, due to inconsistent switches between left-to-right and right-to-left order:

x = (a * b + user_function(c/d)).builtin_function()

This is something that I think should be improved in order to improve Rust's usability for numerical code. This proposal is one possible way to reach this goal, though not the only one of course.

HadrienG2 avatar May 09 '17 17:05 HadrienG2

It's not just reverse polish notation, but distinguishing between left vs right actions. At least one crypto library briefly confused me because method syntax pushed them into using right actions. And they work with abelian objects. lol This would not provide left actions, but sometimes function call syntax is preferable.

burdges avatar May 10 '17 06:05 burdges

https://github.com/rust-lang/rust/issues/73014 was closed in favor of being able to use-import Default::default directly.

I'd like to provide an additional use-case for motivation: Many libraries define free functions for constructors to mathy types, like vectors.

Some examples: https://docs.rs/glam/0.24.2/src/glam/f32/vec2.rs.html#12 https://docs.rs/egui/latest/egui/fn.pos2.html

It would be nice if instead of having to create wrapper functions, the user (or the lib author with pub use) could just do use Vec2::new as vec2;

crumblingstatue avatar Dec 29 '23 22:12 crumblingstatue

Also note that it is possible to write <_>::default() where type inference is applicable.

SOF3 avatar Jan 02 '24 02:01 SOF3