What is the built-in `#[main]` attribute? - rust

I have been using the #[tokio::main] macro in one of my programs. After importing main and using it unqualified, I encountered an unexpected error.
use tokio::main;
#[main]
async fn main() {}
error[E0659]: `main` is ambiguous
--> src/main.rs:3:3
|
3 | #[main]
| ^^^^ ambiguous name
|
= note: ambiguous because of a name conflict with a builtin attribute
= note: `main` could refer to a built-in attribute
I've been scouring the documentation but I haven't been able to find this built-in #[main] attribute described anywhere. The Rust Reference contains an index of built-in attributes. The index doesn't include #[main], though it does include an attribute named #[no_main].
I did a search of the rustlang/rust repository, and found some code that seems related, but it seems to use a pair of macros named #[start] and #[rustc_main], with no mention of #[main] itself. (Neither of those macros appears to be usable on stable, with #[start] emitting an error that it's unstable, and #[rustc_main] emitting an error that it's only meant for internal use by the compiler.)
My guess from the name would have been that it's meant to mark a different function as an entry-point instead of main, but it also doesn't seem to do that:
#[main]
fn something_else() {
println!("this does not run");
}
fn main() {
println!("this runs");
}
Rust Analyzer didn't have much to offer:
What does the built-in #[main] attribute do, aside from conflicting with my imports? 😉

#[main] is an old, unstable attribute that was mostly removed from the language in 1.53.0. However, the removal missed one line, with the result you see: the attribute had no effect, but it could be used on stable Rust without an error, and conflicted with imported attributes named main. This was a bug, not intended behaviour. It has been fixed as of nightly-2022-02-10 and 1.59.0-beta.8. Your example with use tokio::main; and #[main] can now run without error.
Before it was removed, the unstable #[main] was used to specify the entry point of a program. Alex Crichton described the behaviour of it and related attributes in a 2016 comment on GitHub:
Ah yes, we've got three entry points. I.. think this is how they work:
First, #[start], the receiver of int argc and char **argv. This is literally the symbol main (or what is called by that symbol generated in the compiler).
Next, there's #[lang = "start"]. If no #[start] exists in the crate graph then the compiler generates a main function that calls this. This functions receives argc/argv along with a third argument that is a function pointer to the #[main] function (defined below). Importantly, #[lang = "start"] can be located in a library. For example it's located in the standard library (libstd).
Finally, #[main], the main function for an executable. This is passed no arguments and is called by #[lang = "start"] (if it decides to). The standard library uses this to initialize itself and then call the Rust program. This, if not specified, defaults to fn main at the top.
So to answer your question, this isn't the same as #[start]. To answer your other (possibly not yet asked) question, yes we have too many entry points.

Related

Is there a way to enforce correct spelling of features?

Let's assume I have the following feature defined in Cargo.toml:
[features]
my_feature = []
And the following code lives in src/lib.rs:
#[cfg(feature = "my_feature")]
fn f() { /* ... */ }
#[cfg(not(feature = "my_faeture"))] // <-- Mind the typo!
fn f() { /* ... */ }
How can I enforce, that the feature-strings are matched against the list of both explicitly defined and implicitly available features in Cargo.toml so that for instance typos could be avoided?
When RFC 3013, "Checking conditional compilation at compile time", is implemented, there will be warnings for a #[cfg] referring to a feature name that is not declared by Cargo, just as you're asking for. However, the implementation only just got started (Sep 28, 2021).
The means of operation described in the RFC is just as you suggested, ‘cargo would pass down the flags to rustc’.
It may be worth noting that this will not check all conditions appearing in the source text; as described in the RFC:
This lint will not be able to detect invalid #[cfg] tests that are within modules that are not compiled, presumably because an ancestor mod is disabled.
So, it will not confirm that all #[cfg(feature)] are valid on a single cargo check — you will need to test with your various features or combinations of features. But those are the same combinations that you would need anyway to check for compile errors in all of the regular source code that could be enabled; once you do that, the lint will assure you that you don't have any #[cfg(feature)] that are never enabled due to a typo.

Learning rust getting compile error when declaring None

I'm currently reading the official rust-lang book (the one on their website/documentation) and I'm taking notes by copying code and writing comments for everything. I'm currently on chapter 6, Options enum type. Based on the book and some Rustlings code I came across while googling the following should be possible based on the the official book
let none: Option<i32> = None;
I also have the following notes in comment form next to it :
If we use None rather than Some, we need to tell Rust what type of Option<T> we have, because the compiler can’t infer the type that the Some variant will hold by looking only at a None value. And I mean it satisfies the requirement but I keep getting the following error:
mismatched types
expected enum `main::Option<i32>`
found enum `std::option::Option<_>`
I did come across this which works:
let _equivalent_none = None::<i32>;
Can anyone explain why one works but the other doesn't? The official book doesn't even mention the second variant (that doesn't throw an error). Is the newest version different from what's documented in the book?
It appears that you have defined your own enum called Option in your program. Thus, there are two different types called Option: yours (main::Option), and the standard one (std::option::Option). The variable none has type main::Option, but None is of type std::option::Option.
Obvious solution is to just delete your own enum. If, however, for the sake of experiment you do want to create an instance of your own enum called Option, and assign the value of None to it, you'd need to qualify None:
let none: Option<i32> = Option::None;
The problem is that defining an enum Option { None, … } brings a new Option into scope, shadowing the std::option::Option imported by std::prelude by default. However, enum Option { None, … } does not bring a new None into scope, so the std::option::Option::None imported by prelude is still there.
So, you have two options:
Use: let none: Option<i32> = Option::None;, specifying which none to use explicitly.
Add a use crate::Option::*; below your enum, bringing your own None into scope.

How can I fix the broken use statement in chapter 2 of ``Entirely Too Many Lists''?

If I try to test the final code (including hidden code which wraps the whole file in a main function) of chapter 2 of Learning Rust With Entirely Too Many Lists I get this error:
error[E0432]: unresolved import `super::List` --> src/second.rs:110:9
| 110 | use super::List;
| ^^^^^^^^^^^ no `List` in `second`
Turns out if I remove the hidden code that wraps a main function around the displayed code I can run tests just fine. This does raise the question of what the purpose was of this hidden code that broke the use statement. Also no variation on that use statement (::List, crate::List, List, no use statement) fixes the error if I leave the main function there.
Question: Can the use statement be fixed while the main remains there? If not why is the main function there at all?
Can the use statement be fixed while the main remains there?
No, super will refer to the encompassing module, not the function scope. There is no path that can name it as far as I'm aware. You could wrap everything within main in another module, so super can find it that way:
fn main() {
mod inner {
struct List;
mod tests {
use super::List;
}
}
}
If not why is the main function there at all?
mdBook just mimics the behavior of rustdoc which does wrap code samples with fn main() if not provided. This is not typically a problem since rustdoc examples wouldn't normally have the typical mod tests structure. Its unfortunate that this isn't handled though.

Is there a way to show a warning that a Result is redundant as a return type?

Is there an attribute like #[warn(redundant_result_as_return_type)] to show a warning that Result is redundant as a return type?
#[derive(Debug)]
struct SomeError();
fn process_some() -> Result<(), SomeError> {
Ok(())
}
fn main() {
process_some().unwrap();
}
(playground)
This code produces no warnings despite the fact that Result is not needed as the return type at all.
I've been deciding how to properly implement error handling of a function from the crate used in my project. After digging into the function implementation, it turned out that no errors are generated.
Since then, I want to prevent such cases in my own code. Ideally it would be great to get such warnings from inside of imported crates too, when utilizing methods with redundant Results, but as far as I understand such checking is impossible for used crates without adjusting their code.
The question is in some way the opposite of Possible to declare functions that will warn on unused results in Rust?.
No, there is no such warning, either in the compiler or in Clippy. You could submit an issue to Clippy suggesting they add it, of course.
I challenge that this is a common occurrence or even one that is actually a problem. Even if it was, I'd believe such a warning will have many disadvantages:
I use this pattern to scaffold out functions that I'm pretty sure will return an error once I've implemented them.
A method that is part of a trait cannot change the return type.
You might be passing the function as a function pointer or closure argument and the return type cannot be changed.
Changing the return type breaks API backwards compatibility, so you have to wait until a major version bump.

Optional function argument that is specified as a trait instead of a concrete type

I have been watching Rust for the past few months but I just started into an actual project. I am not sure if the terminology in the title is correct. Please let me know how it can be corrected.
I am writing a rust wrapper around the ENet library (http://enet.bespin.org). My goal is to keep the rust API as similar to the C API as possible except to refactor functions that take C style handle pointers into member functions of structure objects. I want to keep the API similar so that the official C documentation will apply equally well to the rust wrapper.
ENet exposes a single function to create either a client host or a server host. When creating a server you pass a pointer to an IP Address structure to the function. When creating a client you pass NULL.
I am trying to emulate that behavior using the ToSocketAddr trait and Option but I am running into problems using them in conjunction.
This is a reduced example of what I am trying to do:
use std::io::net::ip::ToSocketAddr;
fn create_host<A: ToSocketAddr>(addr: Option<A>) {
match addr {
Some(a) => println!("Address is {}. Return a server host object.",a.to_socket_addr()),
None => println!("no address... Return a client.")
};
}
fn main() {
create_host(Some("localhost:12345"));
create_host(None);
}
The first call to create_host() works like a charm. The second call however will not compile.
Rustc returns
error: unable to infer enough type information about `_`; type annotations required
I am guessing that error is occurring because None doesn't provide any resolution to the generic A. I tried the following but this doesn't work either because ToSocketAddr does not implement the trait core::kinds::Sized.
fn create_host(addr: Option<ToSocketAddr>) {
...
}
Is there a way I can do this or do I need to take a different approach?
fn main() {
create_host(Some("localhost:12345"));
create_host(None::<&str>);
}
I've chosen &str here as it is the same type as on the first call, so that the compiler wouldn't generate another monomorphized version of the generic function. You could choose any type that implements ToSocketAddr.

Resources