Relevant Post: Initial Steps with Rust
Relevant changes to adapt to Rust, coming from a Java background. Contains information about Rust’s environment and the language structures.
For code snippets on how to do specific things as we do in Java, check my Rust experience post
Rust Environment
Cargo
Cargo is Rust’s dependencies manager and most projects should be created and managed with it, for easier use.
Commands I used so far:
cargo new hello_cargo
creates a new project.
Cargo.toml is a file which cargo creates and it contains the project’s metainformation and its dependencies.
cargo build
builds binaries from source code.
cargo run
directly compiles and starts the programm.
cargo install --path .
installs from binaries to your system. Useful to use and test my own programs.
--force
needed if it already was installed
Dependencies
Add new dependency
Just open the file Cargo.toml and add the name under [Dependencies]
. This adds Randoms v0.3.14 into the build.
[dependencies]
rand = "0.3.14"
What is Ownership
In Rust memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. It addresses keeping track of which parts of code are using what data on the heap, minimizing the amount of duplicate data on the heap, and cleaning up unused data on the heap so the user doesn’t run out of space.
This is one of Rust’s central features and takes some time to get used to.
Stack vs Heap
They are both parts of memory that are available to your code to use at runtime, but they are structured in different ways.
stack
it stores values as LIFO (Last in, First Out). All data stored here must have a known, fixed size. Think of it as a stack of plates, where removing plates from the middle of the stack wouldn’t work as well as adding and removing from the top. This is called pushing onto or popping the stack
heap
it contains values with an uknown size at compile time, or a size which may change. The heap is less organized as the stack. When you put data into the heap, you request a certain amount of space. The Operative System finds an empty spot on the heap that is big enough, marks it as being in use, and returns a pointer, which is the address of that location. This is called allocating on the heap. Because this pointer has a fixed, known size, you can store the pointer on the stack, but when you want the actual data, you must follow the pointer.
Pushing values onto the stack is not considered allocating. Pushing to the stack is faster than allocating on the heap, because the OS never has to search for a place to store new data; that location is always at the top of the stack. Accessing data from the heap is also slower because you have to follow a pointer to get there.
When your code calls a function, the values passed into this function and the function’s local variables get pushed onto the stack. When the function is over, they get popped from the stack.
Language Structures
Variables
Create a variable
In Rust, variables are inmutable by default. mut
allows them to be mutable.
Several words-variables are separated by underscore.
// new() is a static method from String
let your_guess: String = String::new();
let mut another_guess: String = String::new();
Shadowing a variable
This is often used to reuse the name of a variable, when we want to change its type without needing to have 2 separated variables, one as my_var
and another as my_var_str
.
let x = 5;
let x = x + 1;
println!("the value of x is: {}", x); // 6
References
(Check code! Change it to a simpler example).
&
indicates that this argument is a reference. This is a way to access a piece of data, without needing to copy it into memory multiple times. Like variables, references are inmutable by default. To do it mutable we’d to write &mut guess
.
let mut guess: String = String::new();
io::stdin().read_line(&mut guess)
.expect("bla");
Constants
They are declared with const
instead of let
. They have to be a literal and may not be the result of a function.
const MAX_POINTS: u32 = 100_000;
The main difference of mutables vs shadowing is with the former, we may change the variable type, as we’re creating a new one with the keyword let
.
Functions
Template
Is it possible to return sooner than at the end of the function with the keyword return
, most functions should return the last expression implicitly though.
fn main() {
let x = plus_one(5);
// do something with x
}
fn plus_one(x: i32) -> i32 {
x + 1 // this is an statement
}
Statements vs Expressions
Statements are instructions that perform some action and do not return a value.
Expressions evaluate to a resulting value. An example of this is the block we use to create new scopes {}
.
fn main() {
let x = 3; // this is an statement.
// this whole block between {} is an expression
let y = {
let x = 6; // this is valid only at this scope
x + 2 // notice there is no semicolon
}
println!("x is {} and y is {}", x, y);
// will print the values 3 and 8
}
Control Structures
if as a let statement
Because if
is an expression, we can use it to the right of a statement.
let condition = true;
// both have to be of the same type.
let number = if condition {
5
} else {
6
};
loop
This creates an infinite loop, which stops with break
. It’s also possible to directly return a value for a statement. Rust also has a while
loop.
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result); // 20
for .. in
for number in (1..4) {
println!("{}", number);
}
Error Handling
Crash on Error
let mut guess: String = String::new();
io::stdin()
.read_line(&mut guess)
.expect("oh no!");
Handling an error
Switching from expect
to a match
expression is generally how an error is handled.
The method .parse()
returns a Result
type, which is an Enum with the variants Ok
and Err
. We use match
to compare against this Result
.
loop {
// ...
let guess: u32 = match guess.trim()
.parse() {
Ok(num) => num,
Err(_) => continue,
}
}
The underscore _
here is a catchall value. In this example, it means we want to catch all Err
values, no matter the information inside them.
Original Reference(s)
(I’m slowly following all chapters here + own learn by doing)
https://doc.rust-lang.org/book/ch01-00-getting-started.html