cargo new --bin myproj
cargo new --lib mylib
cargo build --release
cargo run
rustup doc
- offline docs in the browser
You can add additional binaries by placing them in a bin/ directory.
The build script is simply another Rust file that will be compiled and invoked prior to compiling anything else in the package.
quick-error
error-chain
failure
rental
or owning-ref
crates
let x = 32i64
let pi = 3.141592;
println!("Pi is roughly {:.3}", pi);
"pretty printing" with {:#?}
Variables can be overwritten with shadowing.
0.000_001
and 1_000
are both valid.
println!("one element tuple: {:?}", (5u32,));
let integer = decimal as u8;
// Explicit cast
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
// A unit struct
struct Unit;
// A tuple struct
struct Pair(i32, f32);
// A struct with two fields
struct Point {
x: f32,
y: f32,
}
let bottom_right = Point { x: 5.2, ..point };
use crate::Status::{Poor, Rich};
use crate::Work::*;
//...
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
println!("violets are #{:06x}", Color::Blue as i32);
//...
enum List {
// Cons: Tuple struct that wraps an element and a pointer to the next node
Cons(u32, Box<List>),
// Nil: A node that signifies the end of the linked list
Nil,
}
format!
is similar to print!
, but returns a heap allocated string instead of printing to the console
'outer: loop {
//...
break 'outer;
Return value from the loop: break counter * 2;
Inclusive range: for n in 1..=100 {
match foo {
Foo { y, .. } => println!("y = {}, we don't care about x", y),
match pair {
(x, y) if x == y => println!("These are twins"),
//...
match age() {
n @ 1 ..= 12 => println!("I'm a child of age {:?}", n),
if let Foo::Bar = a {
Closures can capture variables:
- by reference:
&T
- by mutable reference:
&mut T
- by value:
T
Using move before vertical pipes forces closure to take ownership of captured variables
When taking a closure as an input parameter, the closure's complete type must be annotated using one of a few traits. In order of decreasing restriction, they are:
Fn
: the closure captures by reference (&T
)FnMut
: the closure captures by mutable reference (&mut T
)FnOnce
: the closure captures by value (T
)
// A non-copy type.
// `to_owned` creates owned data from borrowed one
let mut farewell = "goodbye".to_owned();
fn create_fn() -> impl Fn() {
let text = "Fn".to_owned();
move || println!("This is a: {}", text)
}
Diverging functions never return. They are marked using !
, which is an empty type.
pub(in crate::my_mod) fn public_function_in_my_mod() {
Functions declared using pub(super)
syntax are only visible within the parent module.
A crate can be compiled into a binary or into a library. By default, rustc will produce a binary from a crate. This behavior can be overridden by passing the --crate-type
flag to lib
.
Libraries get prefixed with "lib", and by default they get named after their crate file, but this default name can be overridden by passing the --crate-name
option to rustc or by using the crate_name attribute.
rustc executable.rs --extern rary=library.rlib
When attributes apply to a whole crate, their syntax is #![crate_attribute]
, and when they apply to a module or item, the syntax is #[item_attribute]
(notice the missing bang !
).
// Explicitly specified type parameter `char` to `generic()`.
generic::<char>(SGen('a'));
Multiple bounds: fn compare_prints<T: Debug + Display>(t: &T) {
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB + TraitC,
D: TraitE + TraitF {}
Struct std::marker::PhantomData
Zero-sized type used to mark things that "act like" they own a T
.
let _box1 = Box::new(3i32);
Those assignments are equal:
let ref ref_c1 = c;
let ref_c2 = &c;
let (_, ref mut last) = mutable_tuple;
T: Trait + 'a:
Type T
must implement trait Trait and all references in T
must outlive 'a
.
<'a: 'b, 'b>
reads as lifetime 'a
is at least as long as 'b
.
The following is a list of derivable traits:
- Comparison traits:
Eq
,PartialEq
,Ord
,PartialOrd
. Clone
, to createT
from&T
via a copy.Copy
, to give a type 'copy semantics' instead of 'move semantics'.Hash
, to compute a hash from&T
.Default
, to create an empty instance of a data type.Debug
, to format a value using the{:?}
formatter.
Interesting destructuring: let &Inches(inches) = self;
if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the dyn
keyword, e.g. Box<dyn Animal>
.
Simplifying type signatures: -> impl Iterator<Item=i32> {
/ CompSciStudent (computer science student) is a supertrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both subtraits.
trait CompSciStudent: Programmer + Student {
//...
Disambiguating calls to method named identically in different traits:
let age = <Form as AgeWidget>::get(&form);
The arguments of a macro are prefixed by a dollar sign $
and type annotated with a designator.
The stringify!
macro converts an ident
into a string.
The expr
designator is used for expressions.
https://doc.rust-lang.org/reference/macros-by-example.html
Macros can be overloaded to accept different combinations of arguments.
?
means unwrap
or return Err(From::from(err))
.
filter_map
calls a function and filters out the results that are None
.
Result
implements FromIter
so that a vector of results (Vec<Result<T, E>>
) can be turned into a result with a vector (Result<Vec<T>, E>
). Once an Result::Err
is found, the iteration will terminate.
// Iterators can be collected into vectors
let collected_iterator: Vec<i32> = (0..10).collect();
A String
is stored as a vector of bytes (Vec<u8>
), but guaranteed to always be a valid UTF-8 sequence. String
is heap allocated, growable and not null terminated.
&str
is a slice (&[u8]
) that always points to a valid UTF-8 sequence, and can be used to view into a String
, just like &[T]
is a view into Vec<T>
.
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
// ...or Unicode code points.
let unicode_codepoint = "\u{211D}";
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// If you need quotes in a raw string, add a pair of #s
let quotes = r#"And then I said: "There is no escape!""#;
let bytestring: &[u8; 21] = b"this is a byte string";
let raw_bytestring = br"\u{211D} is not escaped here";
You can easily implement Eq
and Hash
for a custom type with just one line: #[derive(PartialEq, Eq, Hash)]
Rust provides a mechanism for spawning native OS threads via the spawn
function, the argument of this function is a moving closure.
Read lines efficiently: https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html
Rust provides a Foreign Function Interface (FFI) to C libraries. Foreign functions must be declared inside an extern
block annotated with a #[link]
attribute containing the name of the foreign library.
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
}
Unit tests can return Result<()>
, which lets you use ?
in them.
Test for panics: #[should_panic(expected = "Divide result is zero")]
cargo test test_any_panic
Tests can be marked with the ,#[ignore], attribute to exclude some tests. Or to run them with command cargo test -- --ignored
Syntax for panicing in doccomments: /// ```rust,should_panic
/// # // hidden lines start with
# symbol, but they're still compileable!
Cargo looks for integration tests in tests
directory next to src
.
Each Rust source file in tests directory is compiled as a separate crate.
Dev dependencies are added to Cargo.toml in the [dev-dependencies]
section.
let raw_p: *const u32 = &10;
fn main() {
foo::r#try();
}