Implied "main" function


#1

I think it would be great if the rustc compiler, when compiling a single source file that does not contain a function named “main”, would include automatically “fn main() {” before the contents, and “}” after the contents of that file. For example, if the file contains the lines:

let a = 12;
print("{}", a);

it would be compiled as it were:

fn main() {
    let a = 12;
    print("{}", a);
}

That would have the following advantages:

  • In a tutorial of the language, now even a minimum program must define the “main” function. Defining functions is not the first step you should learn when learning a language. If “main” is added automatically, it is easier to teach the language.
  • In tutorials and in reference texts, a lot of examples are just few lines that may be contained in the “main” function. If you can omit two boilerplate lines, you can make shorter executable examples.
  • To check some lines of code, you jot some lines in a new file or in an online playground. If you can omit two boilerplate lines, you can do it faster.
  • A Read-Eval-Print-Loop interpreter should avoid defining the “main” function.
  • A small application program that doesn’t use external crates or modules can be entirely contained in a “main” function. If the “main” function is implied, one level of indentation is avoided.

#2

The fn main() {...} is arguably a pleonasm since it neither takes arguments nor returns anything.

One could conceive of an implicit function declaration for any code declared at the top level of a file. If in main.rs then compose a fn main(). If in another file then create an anonymous function that is called at some point before main is called.


#3

I think this is an interesting idea, and I definitely see the advantage. But I don’t think we should do it. :slight_smile:

A lot of the ergonomics features we’re considering come down to making the language more incremental to learn. Right now, users often have an experience of needing to learn a huge number of features all at once to understand how to write even basic Rust. So we’d like to be able to build sugar which makes the early stages of learning easier, so that they can gradually ease in to understanding the whole language.

This proposal is definitely along those lines - you can start learning Rust without learning about the main function. So why am I doubtful?

Any change like this has a cost associated with it as well. Since we can’t remove things from the language, adding an ergonomics feature necessarily increases the total number of things you have to learn about the language by the time you’re an “expert” Rust user. So there’s a trade off between making the ‘learning curve’ more gradual, and giving it a greater total height.

I think the gradiance this introduces is less beneficial than the total complexity it adds. The main function does not seem to me like a particularly huge step to have to ascend - you have to learn function syntax and that Rust starts by calling main. But this introduces a lot of complication:

  • What do we do when there is both top-level expressions and a main function?
  • Are items (not expressions) declared in a module with an implied main function visible outside of that module or not? This depends on if those items are ‘inside the main function’ or not.
  • Can implied main functions exist in a crate with submodules or not?
  • Can submodules have top-level expressions? (@repax suggests that they can be & they’d be called before main - to which I’d have to ask, in what order? That seems like a recipe for user confusion to me.)

Probably there are other questions I haven’t thought of as well.

I’m not saying we couldn’t answer all of these questions, and I don’t really have an opinion on what the answer would be. I’m saying that in exchange for not learning about main in their first 15 minutes, users eventually have to learn whatever answers we would come up with. I don’t think that trade off is worth it in this case.

I also think this introduces a tendency toward intermingling the item level and the expression level, a separation which is important to the layering of Rust.


For specific use cases like doctests and a repl, we can come up with specific solutions that aren’t at the language level.


#4

Am I sensing a RustScript?


#5

I hope not. I just mean that the doctest or repl runtime wrap user input in a main function. Importantly the concept of being broken into multiple files (the source of most of the complexity I identified) does not exist for these use cases.


#6

The idea wasn’t even half-baked, tbh. Quarter-baked?

But you mentioned specific use cases where you don’t necessarily want the full power of the language, and you’re potentially willing to trade some for having a means of writing code faster.

Other use cases, like web client programming, also come to mind.

I’m not going to try to defend my quarter-baked idea :slight_smile: but I do wonder about an “easy” mode or a “fast” mode.


#7

I think this would be a useful feature for cargo script, where you often want to just write a quick script for testing or automation.

Edit: and also Rust Playground too … I can’t count the number of times I’ve had to add a dummy fn main() {} just to get it to compile.


#8

I’m against adding it to the language, as it doesn’t simplify much, but adds more complexity (mixed top-level and function-level statements, “main” of submodules, etc.)

However, it’d be a great feature if Rust Playground “autocorrected” missing main error. That could be done without changing the language.