Compile rust code with no_core failed, `start` is a generic function?

Viewed 99

From the Rust Tidbits: What Is a Lang Item?, there is a piece of code, I copied and put in the

src/main.rs

#![feature(no_core)]
#![feature(lang_items)]

// Look at me.
// Look at me.
// I'm the libcore now.
#![no_core]

// Tell the compiler to link to appropriate runtime libs
// (This way I don't have to specify `-l` flags explicitly)
#[cfg(target_os = "linux")]
#[link(name = "c")]
extern {}
#[cfg(target_os = "macos")]
#[link(name = "System")]
extern {}

// Compiler needs these to proceed
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}

// `main` isn't the actual entry point, `start` is.
#[lang = "start"]
fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize {
    // we can't really do much in this benighted hellhole of
    // an environment without bringing in more libraries.
    // We can make syscalls, segfault, and set the exit code.
    // To be sure that this actually ran, let's set the exit code.
    42
}

// still need a main unless we want to use `#![no_main]`
// won't actually get called; `start()` is supposed to call it
fn main() {}

The Cargo.toml is:

[package]
name = "no_core_code"
version = "0.1.0"
edition = "2021"

[dependencies]

I run at the Dir of Cargo.toml:

cargo +nightly build -Z unstable-options

The Error:

error[E0718]: `start` language item must be applied to a function with 1 generic argument
  --> no_core_code/src/main.rs:25:1
   |
25 | #[lang = "start"]
   | ^^^^^^^^^^^^^^^^^
26 | fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize {
   |         - this function has 0 generic arguments

How to solve it?

1 Answers

I found an example of #[lang = "start"] in ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs.

#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
    main: fn() -> T,
    argc: isize,
    argv: *const *const u8,
) -> isize {

Termination is a trait able to provide an i32 (found in ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/process.rs)

pub trait Termination {
    /// Is called to get the representation of the value as status code.
    /// This status code is returned to the operating system.
    fn report(self) -> i32;
}

Then, the first argument of the function qualified with #[lang = "start"] is a pointer to a function with no argument (argc and argv will be considered and stored before main() is invoked) and providing a result from which we can extract an integer to be considered as the process result (from the OS point of view).

Changing your start() function to fn start<T>(_main: fn() -> T, ... seems enough to make your example work.

$ cargo +nightly run
   Compiling no_core_code v0.1.0 (.../no_core_code)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/no_core_code`
$ echo $?
42

You can also reproduce the Termination trait and use it in your start() function, as in the std library.

pub trait Termination {
    fn report(self) -> i32;
}

impl Termination for () {
    fn report(self) -> i32 {
        0
    }
}

#[lang = "start"]
fn start<T: Termination + 'static>(
    _main: fn() -> T,
    _argc: isize,
    _argv: *const *const u8,
) -> isize {
    // 42
    _main().report() as isize
}

Of course, for a more complete example, you should consider using the three arguments.

Related