Click to expand
- Uses
rustupto install the latest version of Rust. - Rust files always end with the .rs extension.
- Uses
rustcto compile rust files. - Functions start with a
fnkeyword.- Parameters go inside parenthesis,
(). - The function body is wrapped in curly brakets,
{}.
- Parameters go inside parenthesis,
- Rust uses four spaces rather than a tab.
- Using a
!means calling a macro instead of a function.println!is a macro that prints a string to the screen.
- Strings use double quotes,
"string". - Uses
;to end lines
Click to expand
- Cargo is Rust`s build system and package manager.
- Uses
cargoto make a new project.cargo buildto build a cargo project.--releaseoption does a compilation with some optimizations.
cargo runto build and run.cargo checkto check a project without generating a binary.
Click to expand
preludelibrary is automatically included to every rust program while compiling them.- To use other types or functions not in the prelude, they must be brought with a
usestatement.iolibrary comes from the standard librarystdand does input/output functions.
- To create a variable, use a
letstatement. - In Rust, variables are immutable by default.
- Use
mutbefore the variable name to make a variable mutable.
- Use
//syntax starts a comment until the end of the line.Stringtype is provided by the standard library.- UTF-8 encoded bit of text.
::syntax indicates that an associated function follows.- Calls
read_lineto get input from the user. &indicates that this argument is a reference.- References are immutable by default.
- Uses
&mutto make it mutable.
- When using the
.foo()syntax, it is best to divide it. read_linemethod returns a value with theio::Resulttype.- The
Resulttypes are enumerations. (Okwhen successful /Errwhen failed) - If the instance of
io::Resultis anErrvalue,expectwill cause to crash the program and display the message that passed as an argument.
- The
{}is a placeholder for printing values.- Before using external crates, they must be included into the
[dependencies]section in.tomlfile.- Cargo understands Semantic Versioning.
- e.g) `^0.5.5^ means any version that has a public API compatible with version 0.5.5.
- With
cargo doc --open, documentation is built and provided by all of dependencies within it. std::cmp::Orderingtype is another enum. (Less, Greter, and Equal)- The
cmpmethod compares two values.
- The
- A
matchexpression is made up of arms.- Each arm consists of a pattern, the
matchlooks through each arm's pattern in turn.
- Each arm consists of a pattern, the
- Rust has a strong, static type system, but it also has type inference.
- Rust allows to shadow the previous value with a new one.
- The
parsemethod on strings parses a string into some kind of number. - The
loopkeyword creates an infinite loop. - Adds
breakto quit the loop.
Click to expand
- Rust has a set of keywords that are reserved for use by the language only.
- Link: Reference
- Constants are not variables.
- It is not allowed to use
mutwith constants. - When a constant is declared using the
constkeyword, its type must be annotated. - Constants may be set only to a constant expression, not the result of a function call or whatever.
- It is not allowed to use
- Rust's naming convention for constants is to use all uppercase with underscores between words.
- Underscores can be inserted in numeric literals to improve readability.
- e.g)
const MAX_POINTS: u32 = 100_000;
- e.g)
- Using shadowing, new variable with the same name can be declared in the different type.
- Data types are divided into two subsets which are scalar and compound.
- Rust is a statically typed language.
- A scalar type represents a single value.
- Integers, floating-point numbers, booleans, and characters.
- The integer type should be annotated like
[SIGN][BITS]- e.g)
u32for unsigned 32-bit long integer,i128for signed 128-bit long integer. - The default integer is
i32.
- e.g)
- Integer literals can be written in any of the forms shown below.
| Number literals | Example |
|---|---|
| Binary | 0b1111_0000 |
| Octal | 0o77 |
| Decimal | 98_222 |
| Hex | 0xff |
Byte(u8 only) |
b'A' |
- All number literals except the byte allow a type suffix(
57u8) and_as a visual seperator. - When compiling in debug mode, Rust checks for integer overflow that cause the program to panic at runtime.
- Panic means that a program exits with an error.
- When compiling in release mode with the
--releaseflag, Rust just performs two's complement wrapping.- With the library
Wrapping, it becomes available to wrap integers explicitly.
- With the library
- Rust's floating-point types are
f32andf64.- The default type is
f64.
- The default type is
- Rust supports the basic mathematical operations.
+for addition,-for subtraction,*for product,/for division, and%for remainder.- Link: All operators
- A boolean type in Rust has two possible values:
trueandfalse.- Booleans are one byte in size.
- Booleans are specified using
bool. - e.g)
let f: bool = false;
- A character type in Rust is used to represent letters.
- Characters are four bytes in size and represents a Unicode Scalar Value.
- Characters are specified using
char. - Character literals are specified with single quotes, as opposed to string literals, which use double quotes.
- e.g)
let c: char = 'z';
- Compound types can group multiple values into one type.
- Rust has two primitive compound types: tuples and arrays.
- Tuples have a fixed length.
- Once decleared, they cannot grow or shrink in size.
- The tuple is specified with a comma-separated list of values inside parentheses.
- e.g)
let tup: (i32, f64, u8) = (500, 6.4, 1);
- e.g)
- Rust supports pattern matching to destructure a tuple value.
- e.g)
let (x, y, z) = tup;
- e.g)
- Rust also supports direct access to a tuple element by using a period,
..- e.g)
let six_point_four = tup.1;
- e.g)
- Arrays also have a fixed length.
- Every element of an array must have the same type.
- The array is annotated with values of the same type inside square brackets.
- e.g)
let a: [i32; 5]
- e.g)
- Array data are allocated on the stack.
- An element of an array can be directly accessed with an index inside square brackets.
- e.g)
a[0]
- e.g)
- Rust panics at index out of bounds in runtime.
Click to expand
- Function definitions start with the
fnkeyword and have a set of parentheses after the function name. - Rust code uses snake case as the conventional style for function and variable names.
- Rust doesn't care where functions are defined.
- Functions can also be defined to have parameters, which are special variables that are part of a function's signature.
- In function signatures, The type of each parameter must be declared.
- Statements are instructions that perform some action and do not return a value.
- e.g)
let y = 6;, function definitions
- e.g)
- Expressions evaluate to a resulting value.
- Expressions do not include ending semicolons.
- e.g) math operations, calling a function/macro,
{}
- The return value of a function must be declared with its type after
->. - It can be either using the
returnkeyword or the final expression in the block to pass the return value.
Click to expand
- An
ifexpression allows to branch the code depending on conditions.ifexpressions start with the keywordif, which is followed by a condition.
- Conditions of
ifexpressions must be provided as Boolean type. - Rust only executes the block for the first true condition.
- It is more powerful to use
matchthan using too manyelse ifexpressions. - Because
ifis an expression, it can be placed on the right side of aletstatement.- e.g)
let number = if condition { ... } - In this case, types of last expressions in
if,else if, andelseblocks must be equal.
- e.g)
- Rust has three kinds of loops:
loop,while, andfor. - The
loopkeyword makes an infinite loop.- To break out of a loop, the
breakkeyword must be placed. - The loop can also return values using the
breakexpression.
- To break out of a loop, the
- The
whileloop has a condition, and it checks the condition by each loops until it became false. - Using
forloop, it is able to loop through each element of a collection without consideration to the bound.- It is more safe to use
forthan to usewhilewhile looping through a collection.
- It is more safe to use
Rangeis a type provided by the standard library.- It generates all numbers in sequence starting from one number and ending before another number.
- e.g) (1..4)
Click to expand
- All data stored on the stack must have a known, fixed size.
- Data with an unknown size at compile time or a size that might change must be stored on the heap.
- An enough spot on the memory is allocated on the heap.
- Ownership rules:
- Each value in Rust has a variable that's called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
- A scope is the range within a program for which an item is valid.
- The string type is stored on the heap.
- String literals are stored on the stack.
- e.g)
let mut s = String::from("hello");
- The double colon,
::, is an operator that allows to namespace methods under the type. - That kind of string can be mutated.
- e.g)
s.push_str(", world!");
- e.g)
- The
Stringtype need to be allocated an amount of memory on the heap.- The memory must be requested from the OS at runtime.
- This can be done by calling
String::from.
- This can be done by calling
- This memory must be returned to the OS after use.
- The memory is automatically returned once the variable goes out of scope.
- The memory must be requested from the OS at runtime.
- When a
Stringvariable goes out of scope, Rust calls adropfunction. - When assigning the stack data of a variable to another variable, Rust makes a copy of this value.
- In case of the
Stringtype, Rust copies only pointers rather than values.- A
Stringis made up of three parts, pointer, length, and capacity.
- A
- Rust does a shallow copy and also invalidates the first variable to avoid a double free error.
- In other words, Rust moves the first variable to the second.
- To copy the
Stringdeeply, use a common method calledclone.- e.g)
let _s = s.clone();
- e.g)
- It's possible to return multiple values using a tuple.
- e.g)
(s, length)
- e.g)
Click to expand
- To prevent the ownership of a variable to be moved into a different scope, use a reference.
- The ampersand,
&, allows to refer to some value without taking ownership of it. - To dereference it, use the dereference operator,
*.
- The ampersand,
- Although the reference goes out of its scope, the variable is not dropped.
- Setting references as function parameters is called borrowing.
- The value of a reference can be modified only when the reference is set to be mutable.
- There can be only one mutable reference of the variable at a time.
- Rust can prevent data races in this fashion.
- A data race happens when three behaviors occur:
- Two or more pointers access the same data at the same time.
- At least one of the pointers is being used to write to the data.
- There's no mechanism being used to synchronize access to the data.
- It is not possible to have a mutable reference while having an immutable one.
- Multiple immutable references can exist simultaneously.
- A reference's scope starts from where it is introduced and continues through the last time that it is used.
- A dangling pointer is that references a location in memory that may have been given to someone else, by freeing some memory while preserving a pointer to that memory.
- In Rust, the compiler guarantees that references will never be dangling references.
- To do so, Rust introduced a new feature, lifetime.
- Slices let you reference a contiguous sequence of elements in a collection rather than the whole collection.
- The
enumeratemethod returns a tuple that has an index and a reference of an item. - A string slice is a reference to part of a
String.- e.g)
let hello = &s[0..5];
- e.g)
- String literals are also slices pointing to that specific point of the binary.
- e.g)
&str
- e.g)
- Other slices work same as string slices.
Click to expand
- To define a struct, write the keyword
structand name the entire struct.- Inside curly brackets, names and types of data being grouped together should be described.
- To use a struct after defining it, create an instance of that struct by specifying concrete values for each of the fields.
- Pairs of the field and the value will be assigned in this form
key: value.
- Pairs of the field and the value will be assigned in this form
- To get a specific value from a struct, use dot notation.
- e.g)
user1.email = String::from("anotheremail@example.com"); - c.f) Note that the entire instance must be mutable for values to be changed.
- e.g)
- Rust doesn't allow to mark only certain fields as mutable.
- If the parameter names and the struct field names are same, the field names can be omitted.
- Using struct update syntax, it is easily done to create a new instance of a struct that uses most of an old instance's values.
- The syntax
..specifies that the remaining fields not explicitly set have the same value as the given instance.
- The syntax
- Tuple structs have the added meaning the struct name provides but don't have names associated with their fields.
- e.g)
struct Color(u8, u8, u8);
- e.g)
- To print out debugging information, the annotation
#[derive(Debug)]should be added just before the struct definition.- The placeholder should be also changed into
{:?}for single line print or{:#?}for multiple line print.
- The placeholder should be also changed into
- Link: Derivable Traits
Click to expand
- Methods are defined within the context of a struct(or an enum or a trait object).
- Their first parameter is always
self, which represents the instance of the struct the method is being called on.
- Their first parameter is always
- To define the method within the context of a struct, write an
implblock and implement it in this block.- Use method syntax to call a method: add a dot followed by the method name, parentheses, and any arguments.
- e.g)
rect1.area()
- To have a method that changes the instance, use
&mut selfas the first parameter, otherwise&self. - To have a method that takes ownership of the instance, use
selfas the first parameter.- c.f) But this is very rare case.
- Associated functions don't take
selfas a parameter, for they don't have an instance of the struct to work with.- e.g)
String::from()
- e.g)
Click to expand
- To define an enumeration, use
enumkeyword and write the variants of the enum inside curly brakets.- e.g)
enum IpAddrKind { V4, V6 }
- e.g)
- The variants of the enum are namespaced under its identifier so they should be separated by using a double colon.
- e.g)
let four = IpAddrKind::V4;
- e.g)
- The variants of the enum can have associated values.
- e.g)
enum IpAddr { V4(String), V6(String) }
- e.g)
- Each variant can also have different types and amounts of associated data.
- e.g)
enum IpAddr {V4(u8, u8, u8, u8), V6(String)}
- e.g)
- This IpAddr enum is already in use.
- Link: Enum IpAddr
- It is able to define methods on enums using
impllike on structs. - Rust doesn't have the null feature.
- But it does have an enum that can encode the concept of a value being present or absent with the
Option<T>enum. <T>is a generic type parameter.- e.g)
enum Option<T> { Some(T), None, }
- But it does have an enum that can encode the concept of a value being present or absent with the
- Link: Enum Option
Click to expand
- The
matchoperator allows to compare a value against a series of patterns and then execute code based on which pattern matches.- The expression after the
matchkeyword ca be any type.
- The expression after the
- An arm has two parts: a pattern and some code.
- Patterns can be made up of literal values, variable names, wildcards, and so on.
- When the
matchexpression executes, it compares the resulting value against the pattern of each arm, in order. - The code associated with each arm is an expression and the resulting value of it gets returned for the entire
matchexpression. - Matches in Rust are exhaustive.
- Every possibility must be covered in order for the code to be valid.
- The
_placeholder can cover default arms. - The
()is just the unit value. - Link:
unitType
Click to expand
- The
if letsyntax combinesifandletinto a less verbose way to handle values that match just one pattern while ignoring the rest. - The
if letandelseis the same as thematchblock with one arm for the first pattern and another for the rest.- It means that the block of code that goes with the
elseis exactly same as the block of code that would go with the_case in thematchexpression.
- It means that the block of code that goes with the
Click to expand
- A crate is a binary or library.
- The crate root is a source file that the Rust compiler starts from and makes up the root module of the crate.
- A package is one or more crates that provide a set of functionality.
- A package contains a Cargo.toml file that describes how to build those crates.
- Rules for what a package can contain:
- A package must contain zero or one library crates, and no more.
- A package can contain as many binary crates as possible, but at least one crate(either library or binary).
- Cargo follows a convention that src/main.rs is the crate root of a binary crate with the same name as the package.
- If the package directory contains src/lib.rs, Cargo recognizes src/lib.rs as its crate root.
- A package can have multiple binary crates by placing binaries in the src/ directory: each file will be a separate binary crate.
Click to expand
- Module helps to organize the code and controls the privacy of items.
- Public items can be used by outside code.
- Private items are not available for outside use.
- A module can be defined by starting with the
modkeyword, and then the name of the module must be specified. - A module tree shows how modules are related.
- To use an item in a module tree, its path should be written.
- A path can take two forms:
- An absolute path starts from a crate root by using a crate name or a literal crate.
- A relative path starts from the current module and uses
self,super, or an identifier in the current module.
- Both are followed by one or more identifiers separated by double colons,
::.
- All items(functions, methods, structs, enums, modules, and constants) in Rust are private by default.
- Items in a parent module can't use the private items inside child modules.
- Items in child modules can use the items in their ancestor modules.
- Private items in child modules can be exposed to their ancestor modules by using the
pubkeyword. - Using super, items in child modules can use the items in their parents.
- Structs and enums can also be public.
- Struct's fields will still be private.
- All of enum's variants become public.
Click to expand
- Data which collections point to is stored on the heap.
- A vector, string, hash map are commonly used collections in Rust programs.
- To create a new, empty vector, use
Vec::newfunction.- Vectors must have data with the same type.
- e.g) `let v: Vec = Vec::new();
- e.g) `let v = vec![1, 2, 3];
- To reference an element in a vector, there are two ways:
- Using square brackets:
&v[2] - Using a method:
v.get(2)
- When index out of bounds error occured, the program panics in 1. case but not in 2. case.
- Using square brackets:
- With an enum, the vector can store multiple types.
- Rust has only one string type in the core language, which is the string slice
str. - Both
Stringand the string slice are UTF-8 encoded. - Many of the same operations available with
Vec<T>are available withStringas well.- e.g)
let mut s = String::new();
- e.g)
- Or can create a
Stringfrom a string literal.- e.g)
let s = "initial contents".to_string(); - e.g)
let s = String::from("initial contents");
- e.g)
- To update a string, there are two ways:
- Using a method,
push_str(&str),push(&char) - Using a
+operator:fn add(self, s; &str) -> String {...}
addtakes ownership ofself.
- Using a method,
- Rust compiler can coerce the
&Stringinto a&str. - Rust doesn't support indexing.
- A
Stringis a wrapper over aVec<u8>. - But each unicode scalar value takes multiple bytes..
- So, directly indexing to a
Stringelement returns an unexpected value.
- A
- Slicing
Stringreturns&str. - Hash maps are homogeneous.
- All of the keys must have the same type, and all of the values must have the same type.
- To construct a hash map:
- Use
insertmethod:scores.insert(String::from("Blue"), 10)); - Use iterator and
collectmethod:teams.into_iter().zip(vec_str, vec_score);
- Use
- Only one value can be associated with a key.
- Link
std::collections
Click to expand
- Rust has the type
Result<T, E>for recoverable errors - Rust also has the
panic!macro that stops execution when the program encounters an unrecoverable error. - The call to
panic!causes the error message that shows the place where the panic occurred.- With the
RUST_BACKTRACEenvironment variable, the error can be backtraced.
- With the
- With using
Result<T, E>, recoverable errors are easily interpreted in runtime.- Different errors can also be treated differently by using
match {}.
- Different errors can also be treated differently by using
unwrap()method returns the value inside theOk.expect()method is similar tounwrap()but it can convey the error message that is provided bypanic!.- Instead of handling the error within the function, the erorr can be propagated to the calling code.
- Using the
?operator, the implementation of propagating error can be much easier.
- Using the