RUST · STEP 1 · INTRO
Rust · Intro to the language
Rust 1.99.1 (any 1.85+ works): variables, input, conditionals, loops, vectors, hash maps and functions — each shown first in the JavaScript you know, then in Rust.
What you will learn#
- Creating Variables in a New Language
- Conditionals in a New Language
- Looping in a New Language
- Collections in a New Language
- Writing Functions in a New Language
Getting Started#
Install Rust with rustup, the official installer, from rustup.rs. It gives you the compiler (rustc), the package manager and build tool (cargo) and keeps them updated. Current stable is 1.99.1; anything from 1.85 up runs everything on this page.
rustc --versioncargo new hello_rustcd hello_rustcargo runcargo new makes a folder with a Cargo.toml (Rust's package.json) and a src/main.rs whose fn main() is the program's entry point. cargo run compiles and runs it. Unlike JavaScript, Rust is compiled: typos are reported before anything runs, which is a feature once you get used to it.
If you'd rather not install anything yet, the official Rust Playground runs every block on this page in the browser.
Creating Variables#
How we did it in Javascript
// We create constant variables with the const keywordconst myConstant = 5;// We create re-assignable variables with the let keywordlet myVariable = "Hello World"; // We print variables to the console using console.logconsole.log(myConstant);console.log(myVariable);How we do this in Rust
fn main() { // Variables are immutable by default; the type is inferred (i32 here) let my_constant = 5; // `mut` makes a variable re-assignable; String::from builds an owned, growable string let mut my_variable = String::from("Hello World"); // println! is a macro (the !). {} is a placeholder, and you can name the variable inside the braces println!("{}", my_constant); println!("{my_variable}"); my_variable = String::from("Hello Rust"); println!("{my_variable}");}Two things that trip up JavaScript developers:
let x = 5is immutable —x = 6later is a compile error. Addmutwhen you intend to change it. A realconstalso exists (const MAX_TURTLES: u32 = 10;) but needs a type and a value known at compile time.- Every value has a type. Rust usually infers it, and you can state it:
let count: i32 = 5;(i32= 32-bit integer,u32= unsigned,f64= float,bool,&str/String= text).
Receiving Input#
In JavaScript in the browser, input comes from forms or prompt. In Node, reading from the terminal goes through the readline module; the promise-based version reads cleanly with await:
// Node: ask a question in the terminal and wait for the answerimport readline from "node:readline/promises";import { stdin as input, stdout as output } from "node:process"; const rl = readline.createInterface({ input, output });const name = await rl.question("Who are you? ");console.log(`Hey there ${name}!`);rl.close();How would we do that in Rust?
use std::io; fn main() { // A mutable, empty String to read the line into let mut input = String::new(); println!("Who are you?"); // read_line returns a Result; expect() stops the program with that message if reading fails io::stdin() .read_line(&mut input) .expect("failed to read line"); // the newline the user typed is still in the string, so trim it off println!("Hey there {}!", input.trim());}&mut input lends the string to read_line so it can write into it: that & is a reference, and the borrowing rules around it are the big idea you'll meet in chapter 4 of the Book. Input is always text; input.trim().parse::<u32>() turns it into a number (and returns a Result, because the user may have typed "banana").
Conditionals#
If statements in Javascript
const num = 5; if (num > 3) { console.log("num is greater than 3");} else if (num > 1) { console.log("num is greater than 1");} else { console.log("num is 1 or less");}If statements in Rust
fn main() { let num = 5; // no parentheses around the condition; the braces are required if num > 3 { println!("num is greater than 3"); } else if num > 1 { println!("num is greater than 1"); } else { println!("num is 1 or less"); } // if is an expression, so it can produce a value (the JavaScript ternary) let parity = if num % 2 == 0 { "even" } else { "odd" }; println!("{num} is {parity}");}The condition must be a real bool — if num {} is a compile error, there is no truthy/falsy. Operators are the familiar ==, !=, <, >=, &&, ||, !; there is no === because == never converts types.
While Loops#
In Javascript
let counter = 0; //This will loop 10 timeswhile (counter < 10) { console.log(counter); counter += 1;}In Rust
fn main() { let mut counter = 0; // this will loop 10 times (Rust has no ++, use += 1) while counter < 10 { println!("{counter}"); counter += 1; }}The loop you'll write most is for over a range or a collection. 0..10 is 0 through 9; 0..=10 includes 10. loop {} runs forever until you break.
fn main() { for counter in 0..10 { println!("{counter}"); }}10 Minute Exercise#
Write the code to do the following:
- Loop 10 times starting the counter at 0
- On each loop if the counter is even print "it's even"
- If odd, print "meow" if the number is divisible by 3
- Otherwise print nothing
Collections#
In Javascript we have arrays and objects
const myArray = [1, 2, 3]; console.log(myArray); //logging the entire arrayconsole.log(myArray[0]); // logging an individual item const myObject = { cheese: "gouda", bread: "rye",}; console.log(myObject); //logging the entire objectconsole.log(myObject.cheese); // logging a property using dot notationconsole.log(myObject["bread"]); // logging a property using square bracket notationIn Rust we have vectors and hashmaps
// HashMap is in the standard library but not in scope by defaultuse std::collections::HashMap; fn main() { // vec! builds a Vec<i32>: a growable array where every item has the same type let my_vector = vec![1, 2, 3]; println!("{:?}", my_vector); // {:?} is the "debug" format, which can print whole collections println!("{}", my_vector[0]); // one item by index // HashMap<&str, &str>: keys and values are text here let mut my_hashmap = HashMap::new(); my_hashmap.insert("cheese", "gouda"); my_hashmap.insert("bread", "rye"); println!("{:?}", my_hashmap); // whole map (order is not guaranteed) // get returns an Option: Some(&value) if the key exists, None if it doesn't println!("{:?}", my_hashmap.get("cheese")); // match makes you handle both cases; forgetting one is a compile error match my_hashmap.get("bread") { Some(value) => println!("The value of the bread is {value}"), None => println!("no bread in this hashmap"), }}There is no undefined in Rust. Anything that might be missing comes back as an Option (Some(x) or None) and anything that might fail comes back as a Result (Ok(x) or Err(e)); match is how you take them apart. A Vec must hold one type, so the JavaScript habit of mixing numbers and strings in one array becomes a struct or an enum (next lesson).
10 Minute Exercise#
Look up the methods on Vec in the standard library docs and apply them to the following list.
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];- How to find a list's length (
len) - How to loop over a list (
for n in &numbers) - How to add and remove items from a list (
push,pop,insert,remove) — the vector will need to bemut
Functions#
in Javascript
const addNums = (x, y) => { return x + y;}; console.log(addNums(5, 5));In Rust
// parameters and the return type are always written out; -> i32 is the return typefn add_nums(x: i32, y: i32) -> i32 { // the last expression without a semicolon is the return value x + y} fn main() { println!("{}", add_nums(5, 5));}return x + y; works too, but Rust code leans on the implicit return of the final expression. Closures are the arrow functions: let add = |x: i32, y: i32| x + y; and they are what you hand to map, filter and friends on iterators.
Functions are snake_case, types are CamelCase, constants are SCREAMING_CASE — the compiler warns when you stray.
10 minute exercise#
Write the following functions
sub_numsthat takes two arguments and returns their differencesay_hellothat takes a name as an argument and says hello to that namesay_hello_advthat takes aHashMapwith a name and age property and prints "hello {name}, how does it feel to be {age} years old"loopertakes one vector as an argument, loops over the vector and prints each item individually
Resources to Learn More#
- The Rust Programming Language — the official book; chapters 3 (variables, control flow, functions), 4 (ownership) and 8 (collections) cover this page
- Rust by Example — the same ground as runnable snippets
- Standard library docs —
Vec,HashMap,String,Option,Resultandstd::io
See also