I first came across Rust through a project named Servo. Rust was born out of an attempt to rewrite the rendering engine behind Firefox. Since then it has been voted the “most loved language” at least two years in a row (see Stack Overflow Developer Survey 2017).
The best way to learn Rust is by reading the second edition of the official book. I purchased a copy of the book and the following are notes I took while reading the book or other resources.
One of the first things you’ll notice immediately is that println
is a macro, not a function. More generally, this is how metaprogramming is done in Rust. For example, if you would like to write a routine that takes a variable number of arguments or has default values you’ll need to write a macro.
Another important bit of syntax is the semicolon, which terminates an expression. The value of the final expression in the block of the body of a function is the return value.
Cargo can be used to create a project, build the code, download and manage dependencies. This becomes increasingly important because the standard library in Rust is relatively small. When creating a project use --bin
to specify building a application. Cargo will initialize a git repository by default. It manages dependencies for a project by generating a Cargo.lock
file.
std::prelud
const
, named with all caps/underscores, always annotated with a type, and are always immutablelet
statement::
in String::new()
indicates that new
is an associated function, which is like a static methodnew()
is just a convention&
to indicate a reference, but it is much safer and easier to useResult
is another convention that can be found throughout the standard library. It is an enumeration, which consists of variantscargo doc --open
will generate documentation for all of your dependencies and open them in your browsermatch
expression is made up of arms that consist of a pattern and the code that should be run if the value matches.char
type can represent a broad range of Unicode scalar values, which doesn’t necessarily match intuition of what a character isf64
_
as a visual separatorfmt::Debug
/{:?}
and fmt::Display
/{}
are available for anything in the standard library. Otherwise add #[derive(Debug)]
to the line before a definition. fmt::Display
needs to be manually implementedx = y = 6
, which would assign 6
to both x
and y
in C{}
creates a new scope and is also an expression because it may return a value. This is also true of if
statements! Note that when assigning the return value of a if
statement to a variable it must be followed by a else
statement otherwise the return value is ambiguous if the first conditional is false.Copy
trait, while nothing that requires allocation or is some form of resource doesstr
manages an immutable pointer to memory, while String
handles allocating and deallocating memory when expanding to shrinking the size of a string. It can be thought of as String
holding a str
. It is not possible to create a str
explicitly although it is the type returned by slices and string literals. A str
is often used as a &str
, which is borrowed from a String// A string literal is simply an immutable String reference
let sl = "Hello world";
// Create a String from a string literal
let s = String::from("Hello world");
let mut msr = &s[..];
or let mut msr = &"Hello world"[..];
clear()
the String.struct
can be used when initializing a struct
instance. This is primarily helpful in functions where the parameters match the field names. This is known as field init shorthand.struct Person {
: String
name}
let name = String::from("John");
{name}; Person
struct
update syntax allows creating a new instance of a struct
based on a current instance, while changing some fields. The final line in the block includes ..
followed immediately by the name of the current instance.struct
s allow you to define a struct
without field names if you want two struct
s to have the same members but different typesstruct
s don’t have any fields and behave like the unit type, which is what is returned by expressions that don’t return anythingstruct
that takes &mut self
as a parameter, the instance also needs to be mutable. If self
is a parameter then the method takes ownership of the object, which may be used to transform the object. A method that does not take self
as a parameter is referred to as an associated function, which is called using the struct
name and ::
.struct
each variant of an enum
may store data (e.g. no data, anonymous struct
, String
, tuple). Also, a enum
may define methods.enum Option<t> { Some(T), None, }
, which is included in the prelude as well as it’s variantsif let
is a nice alternative to match
for more concise code, but less checkingmod.rs
, which is in turn inside a directory with the same name as the modulemod {}
or as a file module-name.rs
, which can then be included using mod module-name
.pub
), it can be accessed through any of its parent modules, however if it is private then it can only be accessed by its immediate parent module and any of the parent’s child modulesuse
(e.g. use a::nested::module
). Bring an enum
s variants into scope using {}
(e.g. use AdditivePrimaries::{Red, Green};
) or glob (e.g. use AdditivePrimaries::*;
). Access a parent using relative to the root using ::a::nested::module
or using super::module
let v: Vec<u64>
), the compiler an infer it. For more convenience use the vec!
macro (e.g. let v: = vec![1, 2, 3];
)[]
(e.g. &v[2]
) will panic!
if an element at that index does not exist. Alternatively, use .get()
, which returns a Option<&T>
.*
to get the underlying valuepush_str()
will add the reference to another String, but I would think that it does a copy if the original String is mutable? We already have a mutable reference and then we try to take an immutable one?HashMap
s are a bit cumbersome and not as well supporteduse std::collections::HashMap;
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
static mut
variables that are similar to globals, but their use must be in a unsafe function or block