Hello World!
Here’s one of the simplest vexide programs that can be written, printing a short message to the terminal:
#![no_std]#![no_main]use vexide::prelude::*;#[vexide::main]async fn main(peripherals: Peripherals) { println!("Hello World!");}
Let’s go through this one part at a time.
#![no_std]
and #![no_main]
These two declarations at the top of our file tell the rust compiler two things:
- We don’t want the Rust standard library (
std
crate), sincestd
operates under the assumption of an operating system enviornment. - We don’t want a Rust program entrypoint, since that assumes the existence of a C runtime (something we don’t have).
Both of these are necessary for programs to run on the V5’s embedded hardware. Rather than std
we will be using the core
crate (which provides most functionality of std
), and vexide implements its own entrypoint and startup process that doesn’t require a C runtime.
The vexide Prelude
#![no_std]#![no_main]use vexide::prelude::*;#[vexide::main]async fn main(peripherals: Peripherals) { println!("Hello World!");}
This piece of code brings vexide’s prelude module into scope. All that this does is import a bunch of commonly used types for you, so you don’t have to type their full name every time. For example, you can simply use Motor
rather than vexide::devices::smart::motor::Motor
.
The Program Entrypoint
All vexide programs begin and end at the main
function.
#![no_std]#![no_main]use vexide::prelude::*;#[vexide::main]async fn main(peripherals: Peripherals) { println!("Hello World!");}
You’ll see three key differences in this version of the main
function compared to a normal main
function in Rust:
main
is anasync fn
. vexide ships with its own async runtime that serves as the basis for its multitasking features. In order to run other async functions,main
itself must also be async.main
takes aperipherals
argument. This is an instance of thePeripherals
struct that allows you to create devices and interact with hardware. You can read more about that here.main
is annotated with#[vexide::main]
. This is actually a macro that sets up the real program entry. Behind the scenes, it starts up vexide’s async executor and handles the startup process before running your code.
Returning Errors from main
Your main
function can also return certain types for error handling purposes:
// Typical `main` functionsasync fn main(peripherals: Peripherals) {}async fn main(peripherals: Peripherals) -> () {}// `main` can never returnasync fn main(peripherals: Peripherals) -> ! {}async fn main(peripherals: Peripherals) -> core::convert::Infallible {}// `main` returns a [`Result`] typeasync fn main(peripherals: Peripherals) -> Result<(), E> { Ok(())}
The valid return types for main
are dictated by vexide_core
’s Termination
trait.
All of these forms of main
technically do the same thing. The only difference at runtime is that entries returning Result
will print out an error message if the Result::Err
variant is returned.
In practice though, being able to return an error from main
can be useful due to allowing the ?
operator to be used with Result
directly in main
for more concise error handling.