Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

1b. Introduction for programmers

jam1garner edited this page Jun 11, 2020 · 2 revisions

The goal of this page is not to give you a comprehensive understanding of Rust, or even really a good one. It's just for helping give a brief introduction to the main things you need to know to write Rust for code mods.

Note: "Rust Playground" links to code are provided to allow you to poke at the code yourself. Rust Playground is an online editor that lets you execute Rust code in your browser and is a great tool for learning Rust, but will not work for anything skyline-rs specific.

Declaring Variables

let x = 1;
let y = "hello";
let z = [1, 2, 3];

In Rust you declare variables using the let keyword. In most cases, you don't even need to specify a type.

Specifying a type

let x: u32 = 1;

You can optionally specify a type by using a colon followed by the type. This is usually not necessary due to Rust having "type inference" which is a means of figuring out the type if enough information is there to figure it out without specifying it yourself.

Primitive Types

  • u8/u16/u32/u64 - unsigned integers. The number following is the number of bits in the number.
  • i8/i16/i32/i64 - signed integers
  • usize/isize - integers of the largest size for the given platform. On the Switch, these are basically u64/i64 respectively.
  • *const T - a immutable pointer to type T (example: *const u32 is a pointer to a u32)
  • *mut T - a mutable pointer to type T

Immutability (code link)

let x = 5;
x += 1; // error: cannot assign twice to immutable variable `x`

By default, variables are immutable, you can't modify the value.

Mutability (code link)

let mut x = 5;
x += 1;

Using the mut keyword, you can mark values as mutable so that they can be modified after creation. Code that uses overuses mutability tends to be more likely to be buggy, so try to avoid it!

Rebinding (code link)

let age = 5;
let age = age.to_string();

"Rebinding" is the act of reusing the same variable name. Often (such as in the example) this is useful for when you've transformed the same information in a different form.

Functions

fn get_number_plus_one(number: u32) -> u32 {
     number + 1
}

To declare a function you use the fn keyword, followed by a name (in this case get_number_plus_one), then in parenthesis the arguments (first the argument name, then a colon, then the type). Arguments are separated by commas. After arguments optionally comes the return type, in the form of -> Type.

If the last line of a function does not have a semicolon, then the expression on that line is returned. In the above example, the value of number + 1 is returned. Early returns from functions must use the return keyword like in many languages.

Printing

let x = 3;
let y = 5;
println!("x = {}, y = {}", x, y);

In rust you use println! (a macro, you can tell by the ! at the end) to print text out to the console. You can use {} to indicate a placeholder for a value. Then after your format string, you put all your variables in order.

Borrowing

In Rust, efforts are made to prevents certain types of errors. In order to do that there is a system calling "ownership" and "borrowing". To explain, let me first give an example:

Lets say you own a TV and you're having some friends over. The beautiful thing about a TV is multiple people can watch at the same time. Your friends can "borrow" your TV, so to speak, by watching it. They don't take ownership of your TV just because they are watching it, but they are still capable of observing what is happening on the TV. However if you let all of your friends be able to modify the TV (for example changing the channel), then it will be absolute chaos. You'll miss parts of shows, people might get confused as you switch from one to another, etc. However, when nobody is in the middle of watching a show, you might hand over the remote to someone and tell them to pick out something to watch. Nobody is watching anything, so no chaos, but you're still giving over control over the TV even if you own it.

Ok enough about TV. How does this tie back to Rust?

In Rust, a certain function will own any given piece of data. If you pass an owned piece of data to a function, you give it away (called "moving" it). However you can also let another function "borrow" the data. You still maintain ownership, but the function can either inspect the value (for any type of reference) or modify it (for a mutable reference). Immutable references are like letting your friends watch a show—you can't just change the channel in the middle of it, but you can let as many people watch as you'd like. Mutable references are like handing over your remote to a friend—they can change the channel, but only because nobody else is using the TV. This prevents anyone from having their show interrupted.

Borrowing example

fn main() {
    let mut my_num = 5;
    increment(&mut my_num);
    println!("Number: {}", my_num);
}

fn increment(number: &mut u32) {
    *number += 1;
}

Ok so there's a lot of new syntax being introduced here so lets break it down.

increment(&mut my_num);

Here we use &mut to say "borrow the following value mutably". This makes a reference to the value that allows modification and passes it to the increment function.

fn increment(number: &mut u32) {

The argument for this function is of type &mut u32, meaning it's a mutable (mut) reference (&) to a u32. This essentially indicates to us that this function will be modifying our number, even without looking at its contents.

*number += 1;

The * operator is the opposite of the & operator. While the & creates a reference from a value, the * operator (the reference operator) takes a reference and retrieves the value at that location. If we do *&3, for example, that is a pointlessly long way of just saying 3.

Conditionals

if /*condition*/ {
    // code
} else {
    // other code
}

Loops

// prints the numbers from 0 to 3
for i in 0..3 {
    println!("i = {}", i);
}

This is a for loop. It iterates through a series of items.

The for loop syntax is

for /*variable*/ in /*iterator*/ {
    // code goes here
}

loop {
    println!("Hello!");
}

This is a loop. It runs over and over. If you run a break statement inside of it, it exits the loop.


let mut x = 0;
while x != 3 {
    x += 1;
}

This is a while loop. It runs until a condition is no longer met. Syntax:

while /*condition*/ {
    // code
}