Idea: Simpler method-syntax private helpers

I dislike treating free functions as methods. With a method, I have an idea of where to search, and I can go look in the relevant file unassisted by search engines such as rustdoc. I agree with @MajorBreakfast in that there’s a fundamental difference to inherent methods and free functions that are useful in a specific context.

4 Likes

Actually we don't. I too would not like an API string.write_to_file(file). This is based on the intuition that the receiver should be the more special type. There are more file types than there are string types. Similarly, what I would normally put as the receiver of a Rust method I'd also put as the first argument of a function in Haskell, or the first argument of a free function in Rust.

What I'm saying is not that "anything goes" wrt. receivers, or that method call syntax is not about context, it is! What I'm saying that most free functions really are just methods. Take for example:

  • escape_default in std::ascii - Rust , why would it not make sense to ch.escape_default() ?
  • std::cmp - Rust , this is just an alias for a method on a trait; it would never have existed with UMCS; When these sort of duplications happen, it shows a weakness in the language's design.

If we had UMCS, then IMO it makes sense to have the free functions with the right receiver be listed at the bottom of the documentation of the receivers type. In general, I'd be satisfied with being able to click somewhere, get on a special place and find all the relevant functions.

You don't need rustdoc as a search engine to find it; @MajorBreakfast was asking specifically about how to deal with rustdoc, and I gave one possible solution.

With respect to searching for code, I think searching starts with the module (especially for traits), since it corresponds to the file name, and one file may have many types and impls, not just one. I think the searching procedure for free functions would be similar; you start with the name of the function, go to a use statement, go to the module, find it there.

if you see:

use path::to:my::free_fun;

fn stuff() {
    receiver.free_fun(args..);
}

I think it is quite clear where to go looking.

Besides, if you really want to find code fast, you use an IDE which provides "Go to definition". It would work fine to resolve the free function and jump to it; No manual searching at all!

But, you wouldn't see that. You'd see a big block of imports at the top, and the usage maybe down 500 lines later. Then there's things like preludes.

This applies to traits as well and to types -- I don't see the difference here... You can always ctrl + f for the method from the top and chances are pretty high you'd find it quickly.

On a more general note, people should also split their files up and not write 200 line functions (especially not long and deeply indented ones), which is just an invitation for bugs... At 1000 lines, it is time to break up.

I'm not sure I agree, but either way we should look for ways that avoid such complications if possible.

As for lengths, the problem is not function, but file lengths. I just took a look at std/collections/hash/map.rs as a first thought. It's 3643 lines long. And that is not too uncommon in the Rust world.

I don't want to digress too much; but I do think that a function that violates the single responsibility principle needs to be split up until a function can be reasonably read as pseudocode-like -- so it is not just about files.

A function that is 200 lines long is anathema to me and is a sign either of boilerplate or failing to write abstractions; If I see a Haskell function longer than 10 lines it makes me sad and I usually want to apply a solution like Scrap your boilerplate. :wink:

I know, it makes me sad; I find it hurts readability tremendously. It might also be indicative of the language not doing enough to make splitting up functions easy enough; a REPL + global type inference in it (and maybe no-where else..) should hopefully help.

FWIW, for me it’s not very important to be able to call a function with method syntax but for extension traits I often use this crate: https://crates.io/crates/extension-trait

And it works very well!

I think accepting code like fn sorted<T: Ord>(mut self: Vec<T>) would reduce the orthogonality of traits vs functions, and then a lot of free functions would be (re)written with self just in case someone wants to call them like methods… That said, if there is a huge demand for Rust to be able to call functions like methods, I think Centril’s suggestion to change the function lookup resolution would be the best way because it doesn’t influence how functions will have to be written, to be callable like methods.

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