Vecs and Slices
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?
[u8; 8]
Strings
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);
}
Lifetimes
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";
Traits
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,
}
}
}
struct Counter {
count: u32,
}
impl Counter {
fn increment(&mut self, num: u32) {
self.count += num;
}
}
- Implement a trait from this crate for a struct from this crate
- Implement a trait from another crate for a struct from this crate
- Implement a trait from this crate for a struct from another crate
- Implement a trait from another crate for a struct from another crate
Consider these cases, should the Rust compiler allow them?
Generics
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?
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
Dynamic vs Static Dispatch
- many variations of the machine code is generated
- one for each type used with the algorithm
- type specific behavior compiled in
- single variation of machine code is generated
- one supports all types aaaaaa
- type specific behavior handled with call to vtable
Static Dispatch
Dynamic Dispatch
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);
}
More Traits
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> { ... }
[...]
}
Closures
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
- owns or borrows its environment immutably
- may be called multiple times
- borrows its environment mutably
- may be called multiple times
- owns its environment aaaaaaa
- can be called only once
- consumes its envionment on invocation
Iterators
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);
}
Thread Safety
Tread Safely
Example of threading bug prevented by Rust
unsafe auto trait Send {}
unsafe auto trait Sync {}
auto traits
Must:
- Be a marker trait
Implemented if:
- All fields implement the trait
- There is no negative implementation
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();
}
Rc and Arc
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
Unsafe and FFI
let val: u8 = 8;
let my_ptr: *const u8 = &val;
- Dereference a raw pointer
- Call an unsafe function or method
- Access or modify a mutable static variable
- Implement an unsafe trait
- Access fields of unions
Unsafe Code
unsafe fn my_unsafe_fun() {
// unsafe code here
}
fn my_fun() {
unsafe {
// unsafe code here
}
}
unsafe impl Sync for MyType {}
- Unsafe code
- Extern functions
- C -> R
- R -> C
#[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);
}
cxx
Crate:
Rust for C++ Developers: Day 2
By Hans Elias Bukholm Josephsen
Rust for C++ Developers: Day 2
- 280