Learn to Code via Tutorials on Repl.it!

← Back to all posts
A Basic Introduction to Rust
liltaco (204)

A Basic Introduction to Rust

While there is no alternative to reading The Book, I'd like to show a bit of the syntax and basics of Rust here.

Here is a Hello World program in Rust:

fn main() {
    println!("Hello, world!");
}

The reason it's println! and not println is because it's a macro, but macros are far too advanced to be covered here. Rust macros allow you to write code that writes code.

You can print formatted strings, for example:

fn main() {
    println!("Hello, {num}", num=5);
}

This will print Hello, 5.

Variables

Variables are declared using let, e.g. let x = 5;. However, don't be fooled! This is not a dynamically typed language, but the type is inferred. That means that Rust understood x should be an integer.
Normal rust variables defined with let are immutable (you can't do let x = 5; x = 6;). To make them mutable, use let mut x = 5;.
Here's a list of common types in Rust:

  • Signed integers (can be negative) - i8, i16, i32, i64.
  • Unsigned integers (can't be negative) - u8, u16, u32, u64.
  • Floating point - f32, f64.
  • char - A unicode character.
  • String - A string. &str is a string slice, which is used for efficiency in code.
  • bool, which is true and false.
  • (), which is the empty type. It's used a lot.
  • Arrays: [1, 2, 3]. Their length can't be changed.
  • Tuples: (1, true, "hi"). Their length can't be changed. Their types are written like (i32, bool, &str).
  • Vector: A variable size array. vec![1, 2, 3] or Vec::new().

Getting input

This is overcomplicated in Rust for some reason, though it's similar to how it is in Java.

use std::io;
fn main() {
    let mut input = String::new();
    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
}

Let's explain this. The first line, use std::io, is similar to how it works in Java: while you could have written std::io::stdin(), this gives you a shortcut for the rest of your code.
.read_line(&mut guess) - giving a function &mut gives it a mutable reference, so it can change your variables.
.expect("Failed to read line") - basic Rust error handling, which prints "Failed to read line" and stops the program.

Conditionals, loops, and such

if

Here is an example of an if expression (I'll explain that later):

let password = "hi";
if password == "CorrectHorseBatteryStaple" {
    println!("Correct password!");
} else if password == "123" {
    println!("It's not that easy!");
} else {
    println!("Incorrect password!");
}

In Rust, if you don't put a semicolon on the last line, the value is returned. You can also use the usual return. Here is an example of an if expression returning a value:

let b = true;
let x = if b {
    5
} else {
    7
}
// x is 5

while

// prints 1 ... 99
let mut n = 0;
while n < 100 {
    n += 1;
    println!("{}", n);
}

loop

Loop is like while true.

// prints 1 ... 99
let mut n = 0;
loop {
    if n >= 100 {
        break;
    }
    n += 1;
    println!("{}", n);
}

for

For loops are very similar to Python - they only go over iterators. 0..100 is an iterator of all the numbers 0,...,99. It's similar to range(100).

// prints 1 ... 99
for n in 1..100 {
    println!("{}", n);
}

You can also iterate over an array or a vector:

// prints 1 2 3
let arr = [1, 2, 3];
for n in arr.iter() {
    println!("{}", n);
}

Functions

Here is an example of a function:

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn multiply(a: i32, b: i32) -> i32 {
    return a * b;
}

fn printInt(a: i32) -> () {
    println!("{}", a);
}

Structs

Probably the most important feature of Rust, this is what replaces Object Oriented Programming. Let's see an example:

struct Point {
    x: f64,
    y: f64
}

This is a struct with two variables, x and y. Let's put an instance of it in a variable:

let p = Point { x: 3.0, y: 4.0 };

Now, let's add some functions to it:

impl Point {
    fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }
    
    fn length(&self) -> f64 {
        (self.x * self.x + self.y * self.y).sqrt()
    }
}

A function that doesnt take &self is static and doesn't have access to self. We can call them like this:

let p = Point::new(3.0, 4.0);
println!("{}", p.length); // prints 5

Enums

Enums allow us to make types which can store values of many different types, or just be used as flags.
This will be more obvious with an example:

enum MouseEvent {
    LeftClick,
    RightClick,
    Move(Point),
    Scroll(f64)
}

fn show(event: MouseEvent) {
    match event {
        MouseEvent::LeftClick => {
	    println!("Left click");
	},
	MouseEvent::RightClick => println!("Right click"),
	MouseEvent::Move(p) => println!("Move to {x}, {y}", x=p.x, y=p.y),
	MouseEvent::Scroll(y) => println!("Scroll {}", y)
    }
}

fn main() {
    show(MouseEvent::LeftClick);
    show(MouseEvent::RightClick);
    show(MouseEvent::Move(Point::new(3.0, 4.0)));
    show(MouseEvent::Scroll(5.3));
}

There's much more to Rust than this, including:

  • Ownership.
  • Ownership!!!
  • Splitting code to multiple files (modules)
  • Cargo (using third party packages).
  • Traits.
  • Error handling.
  • Concurrency.
    I recommend reading the official book, The Rust Programming Language. It might be a bit of a difficult read, but it's the only way to really know Rust.
Comments
hotnewtop
xxpertHacker (768)
use std::io;
fn main() {
    let mut input = String::new();
    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
}

How about &mut input, and introducing std::Option, and how Rust takes the route of returning errors, more like Haskell, instead of say, C++?

Missed a method call here:

println!("{}", p.length); // prints 5

Otherwise it seems okay.

EpicGamer007 (1472)

Dude, this is pretty awesome!

CodeLongAndPros (1577)

It's correcthorsebatterystaple I think, the capitalization removes entropy.

lisa2006x (10)

Oh that nice! 😊