Greet the World in Rust

Sonu BSonu B
5 min read

Getting Started

To start writing Rust code, we first need to install the Rust compiler. We can do this by following the instructions on the official Rust website.

If you're on a Linux-based system, then running the following command should install Rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Hello, World

    // Rust files have a .rs extension
    fn main() {
        println!("Hello, world!");
    }

Compile using the Rust compiler:

    $ rustc main.rs
    $ ls
    main  main.rs
    $ ./main
    Hello, world!

Let's analyze the code a bit.

  • println! calls a Rust macro. If it had called a function instead, it would be entered as println (without the !).

  • We end the line with a semicolon (;), which indicates that this expression is over and the next one is ready to begin. Most lines of Rust code end with a semicolon.

Cargo

While using rustc for simple programs is acceptable, as your project becomes more complex, it is necessary to manage various options and facilitate code sharing. This is where Cargo comes in - it is Rust's build system and package manager.

With Cargo, you can delegate many tasks, including building your code, acquiring the libraries your code requires, and building those libraries. Therefore, using Cargo can simplify the development process and make managing Rust projects much easier.Creating a project using Cargo

$ cargo new hello_cargo # creates an empty cargo project
$ cargo build # creates a rust binary
    ...
$ ./target/debug/hello_cargo # run the binary
Hello, world!

Folder structure of a Cargo project

.
├── Cargo.lock
├── Cargo.toml
├── src/
│   ├── main.rs
├── target/
    ├── debug
        ├── main
# there's more stuff but this is all we care about, for now
  • Cargo.toml and Cargo.lock are stored in the root of your package (package root). It's comparable to package.json and package.lock in NodeJS.

  • Source code goes in the src directory. The resultant binary from cargo build is generated in target/debug/.

Variables

Variables are defined with let.

In Rust, variables are immutable by default. When a variable is immutable, once a value is bound to a name, you can’t change that value. You can make them mutable by adding mut in front of the variable name.

let x = 5;
x = 6; // ERROR!
// ^^^^^ cannot assign twice to immutable variable

let mut y = 5; // mut makes it mutable
y = 10 // 👍

Constants can be defined with constant. Like immutable variables, constants are values that are bound to a name and are not allowed to change, but there are a few differences between constants and variables.

You aren’t allowed to use mut with constants. The type of the constant must be annotated. We'll discuss more on types in the next section.

const PI: f32 = 3.14;
// can't use mut. Must declare type.

Although you cannot reassign values to immutable variables, you can redefine existing variables, ie. creating new variables with the same name! This is called shadowing. The definition of a variable gets shadowed by the next one.

let x = 23;
println!("{}", x); // 23
let x = x + 1;
println!("{}", x); // 24
{
    let x = "hello";
    println!("{}", x); // hello
}

Data Types

Annotating variables with data types:

let cool_number: u32 = 1000;
let amazing_string: str = "Donkey!";
let dope_character: char = '$'; 
let incredible_boolean: bool = true;

Below are the basic data types supported by Rust.

bool, char, str                 // boolean, character, string
i8, i16, i32, i64, i128         // integer (signed)
u8, u16, u32, u64, u128         // integer (unsigned)
f32, f64                        // float

Integers and Floating point typed variables also involve defining the bit-length of the variable.

For instance, i8 declares an integer which is 8-bits long. This means that 8 bits will be used to represent the integer value. This restricts the value of the number that can be stored in the variable. An i8 can store numbers from -(27) to 27 - 1, which equals -128 to 127.

fn main() {
    let a: u8 = 2300;
}

5 |     let a: i8 = 2300;
  |                 ^^^^
= note: `#[deny(overflowing_literals)]` on by default
  = note: the literal `2300` does not fit into the type `i8` whose range is `-128..=127`
  = help: consider using the type `i16` instead

Compound Data Types

Compound types are a way to group multiple values into one type. Rust offers two primitive compound types: tuples and arrays.

  1. Tuples:

    • A tuple is a way to group together a number of values with varying types into one compound type.

    • Tuples have a fixed length: once declared, they cannot grow or shrink in size.

    • We create a tuple by writing a comma-separated list of values inside parentheses.

// tuples
let my_tup: (u8, i32, char) = (1, 235, 'b');

println("{}", my_tup.0); // 1
println("{}", my_tup.1); // 235
println("{}", my_tup.2); // b
  1. Arrays:

    • An array is another way to have a collection of multiple values. Every element of an array must have the same type, as opposed to what we saw in tuples.

    • An important point to remember, when coming from other languages, is that arrays in Rust have a fixed length.

    • Arrays are useful when the data needs to be allocated on the stack rather than the heap. (More on this in the next blog post.

// arrays
let my_ar = [2366, 12, 6]

println("{}", my_ar.0) // 2366
println("{}", my_ar.1) // 12
println("{}", my_ar.2) // 6

What's next?

Are you ready to dive into the world of Rust? In our next blog post, we will show you what makes Rust so different and awesome compared to other programming languages. Rust has some super cool features that help it avoid memory leaks, bugs, and crashes. This includes concepts like the Borrow-checker, Ownership, Moving values, etc. We will explain how these features work and why they make Rust a joy to code with. Trust us, you don’t want to miss this one!

0
Subscribe to my newsletter

Read articles from Sonu B directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sonu B
Sonu B