Viewing Lists Conveniently
let mut numbers = vec![2, 5, 16];
// let mut numbers = Vec::new();
numbers.push(8);
assert!(vec.len() == 4);
assert!(vec.pop() == Some(8));
for number in &numbers {
println("in vec: {}", number);
}
ptr | |
len | 4 |
capacity | 8 |
Vec
...
fn print_buffer(buffer: &Vec<u8>) {
for num in buffer {
println!("{}", num);
}
}
fn main() {
let buffer: Vec<u8> = vec![2, 4, 8, 16, 32, 64];
print_buffer(&buffer);
}
fn print_buffer(buffer: &[u8]) {
for num in buffer {
println!("{}", num);
}
}
fn main() {
let buffer: Vec<u8> = vec![2, 4, 8, 16, 32, 64];
print_buffer(&buffer);
}
[T]
let buffer = vec![1, 2, 3, 4, 5, 6];
let buffer_slice = &buffer[1..4];
println!("{:?}", buffer_slice);
&mut [u8]
&mut Vec<u8>
vs
When would you use one vs the other?
String
pub fn from_utf8(vec: Vec<u8, Global>)
-> Result<String, FromUtf8Error>
ptr | |
len | 4 |
capacity | 8 |
String
...
String
str
Vec<T>
[T]
let string_literal: &str = "foobar";
fn print_string(string: &String) {
println!("{}", string);
}
fn main() {
let my_string = String::from("foobar");
print_string(&my_string);
}
fn print_string(string: &str) {
println!("{}", string);
}
fn main() {
let my_string = String::from("foobar");
print_string(&my_string);
}
fn print_string(string: &str) {
println!("{}", string);
}
fn main() {
let my_string = String::from("foobar");
print_string(&my_string[1..4]);
}
let string = "foo bar baz";
for character in string.chars() {
println!("char: {}", character);
}
fn passthrough_reference(string: &str) -> &str {
string
}
fn main() {
let string = String::from("foobar");
let string_ref = passthrough_reference(&string);
std::mem::drop(string);
println!("{}", string_ref);
}
error[E0505]: cannot move out of `string` because it is borrowed
--> src/main.rs:11:20
|
9 | let string_ref = passthrough_reference(&string);
| ------- borrow of `string`
occurs here
10 |
11 | std::mem::drop(string);
| ^^^^^^ move out of `string` occurs here
12 | println!("{}", string_ref);
| ---------- borrow later used here
fn passthrough_reference(string: &str) -> &str {
string
}
fn passthrough_reference<'a>(string: &'a str) -> &'a str {
string
}
fn passthrough_reference<'a>(string: &'a str) -> &'a str {
string
}
fn main() {
let string = String::from("foobar");
let string_ref = passthrough_reference(&string);
std::mem::drop(string);
println!("{}", string_ref);
}
fn passthrough_reference(
string_a: &str,
string_b: &str,
) -> &str {
string_a
}
fn passthrough_reference(
string_a: &str,
string_b: &str,
) -> &str {
string_a
}
error[E0106]: missing lifetime specifier
--> src/lib.rs:4:6
|
2 | string_a: &str,
| ----
3 | string_b: &str,
| ----
4 | ) -> &str {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed
value, but the signature does not say whether it
is borrowed from `string_a` or `string_b`
help: consider introducing a named lifetime parameter
|
1 | fn passthrough_reference<'a>(
2 | string_a: &'a str,
3 | string_b: &'a str,
4 | ) -> &'a str {
|
cargo run
fn passthrough_reference<'a>(
string_a: &'a str,
string_b: &'a str,
) -> &'a str {
string_a
}
Finished dev [unoptimized + debuginfo] target(s) in 0.81s
cargo run
let string_literal: &'static str = "foobar";
Defining Shared Behavior
/// A trait which provides the functionality of printing
/// itself to the stdout.
trait Printable {
/// When called, will print the object to stdout.
fn print(&self);
}
struct MyStruct {
number: u32,
}
impl Printable for MyStruct {
fn print(&self) {
println!(
"MyStruct {{ number: {} }}",
self.number,
);
}
}
Rust has a Defa ult trait.
It has a function that should return the default value of a data type.
Implement Def ault for BankAccount so that the balance is initialized to 0.
struct BankAccount {
balance: i64,
}
Default
Default
struct BankAccount {
balance: i64,
}
impl Default for BankAccount {
fn default() -> Self {
BankAccount {
balance: 0,
}
}
}
BankAccount::default()
struct Counter {
count: u32,
}
impl Counter {
fn increment(&mut self, num: u32) {
self.count += num;
}
}
Consider these cases, should the Rust compiler allow them?
struct MyStruct {
[...]
}
impl MyTrait for MyStruct {
fn my_trait_method(&self) {
[...]
}
}
file1.rs
use file1::MyStruct;
fn a_func() {
let my_instance = MyStruct {
[...]
};
my_instance.my_trait_method();
}
file2.rs
Rule:
Traits must be in scope in order to call methods on them!
use file1::MyTrait;
Powerful Generic Data Structures
enum Option<T> {
Some(T),
None,
}
fn main() {
let my_option = Some(5);
match my_option {
Some(num) => println!("Option contained {}", num),
None => println!("Option was None"),
}
}
There is no Either type in the standard library.
An Either type might contain either of two individual types, Left or Right. These two types might be different.
How might we declare such a type using an Enum with type parameters?
enum Either<A, B> {
Left(A),
Right(B),
}
struct Addable<T>(T);
impl<T> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
fn main() {
let a = Addable(1);
let b = Addable(2);
let c = a.add(&b);
}
error[E0369]: cannot add `T` to `T`
--> src/main.rs:6:21
|
6 | Self(self.0 + other.0)
| ------ ^ ------- T
| |
| T
|
cargo run
Will this code compile?
struct Addable<T>(T);
impl<T> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
struct Addable<T>(T);
impl<T> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
cargo run
error[E0369]: cannot add `T` to `T`
--> src/lib.rs:6:21
|
6 | Self(self.0 + other.0)
| ------ ^ ------- T
| |
| T
|
help: consider restricting type parameter `T`
|
3 | impl<T: std::ops::Add<Output = T>> Addable<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
struct Addable<T>(T);
impl<T: std::ops::Add<Output = T>> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
cargo run
error[E0507]: cannot move out of `self.0` which is
behind a shared reference
--> src/lib.rs:6:14
|
6 | Self(self.0 + other.0)
| ^^^^^^ move occurs because `self.0`
has type `T`, which does not
implement the `Copy` trait
struct Addable<T>(T);
impl<T: std::ops::Add<Output = T> + Copy> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
struct Addable<T>(T);
impl<T: std::ops::Add<Output = T> + Copy> Addable<T> {
fn add(&self, other: &Self) -> Self {
Self(self.0 + other.0)
}
}
fn main() {
let a = Addable(1);
let b = Addable(2);
let c = a.add(&b);
println!("{}", c.0);
}
cargo run
$ cargo run
3
struct MyStruct {
private_state: u32,
pub public_state: u32,
}
impl MyStruct {
fn private_method(&self) {
[...]
}
pub fn public_method(&self) {
[...]
}
}
struct MyStruct {
private_state: MyInnerStruct,
pub public_state: u32,
}
impl MyStruct {
fn private_method(&self) {
[...]
}
pub fn public_method(&self) {
self.private_state.do_something()
}
}
Traits!
i32
f32
trait Delayable
BigInt
trait Delayable {
fn sleep(&self);
}
impl Delayable for i32 {
fn sleep(&self) {
[...]
}
}
impl Delayable for f32 {
fn sleep(&self) {
[...]
}
}
use std::fmt::Debug;
fn print_item<T: Debug>(item: &T) {
println!("{:?}", item);
}
use std::fmt::Debug;
fn print_item(item: &impl Debug) {
println!("{:?}", item);
}
use std::fmt::Debug;
fn print_items<T1: Debug, T2: Debug>(
item1: &T1,
item2: &T2,
) {
println!("{:?} {:?}", item1, item2);
}
use std::fmt::Debug;
fn print_items(
item1: &dyn Debug,
item2: &dyn Debug,
) {
println!("{:?} {:?}", item1, item2);
}
use std::fmt::Debug;
fn print_items(
item1: &dyn Debug,
item2: &dyn Debug,
) {
println!("{:?} {:?}", item1, item2);
}
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
[...]
}
struct CountIterator {
current: u32,
to: u32,
}
impl Iterator for CountInterator {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.to {
self.current += 1;
Some(self.current)
} else {
None
}
}
}
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
fn size_hint(&self) -> (usize, Option<usize>) { ... }
fn count(self) -> usize { ... }
fn last(self) -> Option<Self::Item> { ... }
fn advance_by(&mut self, n: usize) -> Result<(), usize> { ... }
fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }
[...]
}
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
trait CakeMaker<Topping> {
type CakeType;
fn make() -> CakeType;
}
// Cake Bases
struct Small;
struct Flat;
// Toppings
struct Glaze;
struct Nothing;
// Cakes
#[derive(Debug)]
struct Cupcake;
#[derive(Debug)]
struct Cookie;
#[derive(Debug)]
struct CookieWithGlaze;
impl CakeMaker<Glaze> for Small {
type CakeType = Cupcake;
fn make()
}
impl CakeMaker<Nothing> for Flat {
type CakeType = Cookie;
}
impl CakeMaker<Glaze> for Flat {
type CakeType = Cupcake;
}
fn main() {
let string = String::from("foobar");
let print_string = || {
println!("{}", string);
};
print_string();
print_string();
}
foobar
foobar
cargo run
fn make_print_string_closure(string: String) -> impl Fn() {
|| {
println!("{}", string);
}
}
fn main() {
let string = String::from("abc");
let closure = make_print_string_closure(string);
closure();
closure();
}
error[E0373]: closure may outlive the current function, but it
borrows `string`, which is owned by the current
function
--> src/main.rs:2:5
|
2 | || {
| ^^ may outlive borrowed value `string`
3 | println!("{}", string);
| ------ `string` is borrowed here
|
note: closure is returned here
--> src/main.rs:1:49
|
1 | fn make_print_string_closure(string: String) -> impl Fn() {
| ^^^^^^^^^
help: to force the closure to take ownership of `string` (and any
other referenced variables), use the `move` keyword
|
2 | move || {
| ^^^^^^^
fn make_print_string_closure(string: String) -> impl Fn() {
move || {
println!("{}", string);
}
}
fn main() {
let string = String::from("abc");
let closure = make_print_string_closure(string);
closure();
closure();
}
cargo run
foobar
foobar
Fn
FnMut
FnOnce
for n in 0..10 {
println!("{}", n);
}
0..10
std::ops::Range {
start: 0,
end: 10,
}
std::ops::Range {
start: 0,
end: 10,
}
let sum: u32 = (1..10)
.map(|i| i * 2)
.sum();
println!("{}", sum);
cargo run
90
let sum: u32 = (1..10)
.map(|i| i * 2) // <-- what type?
.sum();
println!("{}", sum);
let array = [1, 2, 3, 4, 5];
for (idx, item) in array.iter().enumerate() {
println!("{}: {}", idx, item);
}
Tread Safely
Example of threading bug prevented by Rust
unsafe auto trait Send {}
unsafe auto trait Sync {}
Must:
Implemented if:
unsafe auto trait Send {}
unsafe auto trait Sync {}
fn main() {
let a: Rc<Book> = Rc::new(Book {
name: "Dune".into(),
isbn: 441172717,
});
let b = a.clone();
}
ref_count | |
inner | name |
isbn |
ptr |
Rc
a
ptr |
Rc
b
fn main() {
let my_rc = std::rc::Rc::new(0);
std::thread::spawn(move || {
println!("{}", my_rc);
});
}
error[E0277]: `Rc<i32>` cannot be sent between threads safely
--> src/main.rs:4:5
|
4 | std::thread::spawn(move || {
| _____^^^^^^^^^^^^^^^^^^_-
| | |
| | `Rc<i32>` cannot be sent between threads safely
5 | | println!("{}", my_rc);
6 | | });
| |_____- within this `[closure@src/main.rs:4:24: 6:6]`
|
= help: within `[closure@src/main.rs:4:24: 6:6]`, the
trait `Send` is not implemented for `Rc<i32>`
= note: required because it appears within the type `
[closure@src/main.rs:4:24: 6:6]`
cargo run
fn main() {
let my_rc = std::sync::Arc::new(0);
std::thread::spawn(move || {
println!("{}", my_rc);
});
}
cargo run
0
fn main() {
let my_rc = std::sync::Arc::new(0);
std::thread::spawn(move || {
*my_rc += 1;
});
}
error[E0594]: cannot assign to data in an `Arc`
--> src/main.rs:5:9
|
5 | *my_rc += 1;
| ^^^^^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify
through a dereference, but it is not
implemented for `Arc<i32>`
cargo run
fn main() {
let my_rc = std::sync::Arc::new(
std::sync::Mutex::new(0));
let my_rc_cloned = my_rc.clone();
let handle = std::thread::spawn(move || {
*my_rc_cloned.lock().unwrap() += 1;
});
handle.join().unwrap();
println!("{}", my_rc.lock().unwrap());
}
cargo run
1
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::sync_channel(2);
for thread_num in 0..4 {
let tx_instance = tx.clone();
thread::spawn(move || {
for n in 0..2 {
tx_instance.send((thread_num, n)).unwrap();
}
});
}
for _ in 0..8 {
let item = rx.recv().unwrap();
println!("{:?}", item);
}
}
(0, 0)
(0, 1)
(2, 0)
(2, 1)
(3, 0)
(3, 1)
(1, 0)
(1, 1)
use std::path::Path;
use anyhow::Error;
use tokio::{fs::File, io::AsyncReadExt};
async fn read_file_to_string(
path: &Path
) -> Result<String, Error> {
let mut file = File::open("foo.txt").await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
Ok(contents)
}
use std::path::Path;
use anyhow::Error;
use tokio::{fs::File, io::AsyncReadExt};
async fn read_file_to_string(
path: &Path
) -> Result<String, Error> {
let mut file = File::open("foo.txt").await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
Ok(contents)
}
Function Call
Function Return
.await
point
state #0
state #1
state #2
state #3
state #4
enum FutureState {
State0 {
local1: i32,
local2: String,
[...]
},
State1 { [...] },
State2 { [...] },
State3 { [...] },
State4 { [...] },
}
Crate:
let val: u8 = 8;
let my_ptr: *const u8 = &val;
unsafe fn my_unsafe_fun() {
// unsafe code here
}
fn my_fun() {
unsafe {
// unsafe code here
}
}
unsafe impl Sync for MyType {}
#[no_mangle]
unsafe extern "C" fn sum_numbers(buf: *const u32, len: usize) -> u32 {
let slice = std::slice::from_raw_parts(buf, len);
slice
.iter()
.sum()
}
use std::os::raw::c_char;
use std::ffi::CString;
extern "C" {
fn atof(string: *const c_char) -> f64;
}
fn main() {
let c_string = CString::new("1.5").unwrap();
let num = unsafe {
atof(c_string.as_ptr())
};
println!("{}", num);
}
Crate:
Crate:
Crate:
union
std
struct Book {
name: String,
isbn: u64,
}
fn main() {
let a: Box<Book> = Box::new(Book {
name: "Dune".into(),
isbn: 441172717,
});
}
name |
isbn |
Book
ptr |
Box
a
#[test]
fn a_unit_test() {
assert!(1 + 2 == 3)
}
#[test]
fn a_unit_test() {
assert!(1 + 2 == 3)
}
cargo test
running 1 test
test a_unit_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
assert!(cond)
assert_eq!(lhs, rhs)
#[should_panic]
...
https://doc.rust-lang.org/std/string/struct.String.html
fn main() {
let my_option = Some(5);
match my_option {
Some(num) => println!("Option contained {}", num),
None => println!("Option was None"),
}
}
enum Either<A, B> {
Left(A),
Right(B),
}