Every package in Rust has complete documentation. This is automatically generated by the Rust compiler when building the package.
– For *the rust standard library* `std`, you can find the documentation [here](https://doc.rust-lang.org/std/).
– For external crates from [crates.io](https://crates.io), each package has its documentation. For example, [`rand`](https://docs.rs/rand/0.8.4/rand/).
Copyright By PowCoder代写 加微信 powcoder
– For your own package, `cargo doc –open` will bring up the documentation. (so, please write a short description for your code!)
Next time when you are unclear about anything of a package, read their doc!
In this week’s lecture, we will cover:
1. Ownership
2. references
3. borrowing
## Collections
We will explain ownership using *String*, which is a member of Rust collection data type.
### String
let s_literals = “hello”;
let s1 = String::from(s_literals);
let s = &s1;
let s_slice = &s1[..]
There are four common types that relate to string.
– *String*: like `s1`. A String is stored as a vector of bytes (`Vec
– a **pointer** to the memory that holds the contents of the string.
– The **length** is how much memory, in bytes, the contents of the String is currently using.
– The **capacity** is the total amount of memory, in bytes, that the String has received from the allocator.
– *String Literals*: like `s_literals`, String literal is hardcoded into the text of our program. It is immutable and must have known fixed length at compile time. More examples can be found in [Rust by Example](https://doc.rust-lang.org/rust-by-example/std/str.html) and [TRPL](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html). You can regard string literals as a string slice that points to a specific point of the binary.
– The reference of a *String*: like `s`, it allows us to refer to the string without taking ownership of it.
– **string slice**: like `s_slice`. This type allows us to take a specific part of a string (i.e., substring). It consists of two parts: a pointer to the start of the data and a length.
UTF-8 is a variable-length character encoding
– The first 128 characters (US-ASCII) need one byte
– The next 1,920 characters need two bytes, which covers the remainder of almost all Latin-script alphabets, … up to 4 bytes
You may not index a string directly; Rust stops you because you could end up in the middle of a character! (more details at [here](https://doc.rust-lang.org/book/ch08-02-strings.html).)
let s1 = String::from(“hello”);
let h = s1[0]; // rejected
#### Operating a string
let mut s1 = “hello”.to_string();
let mut s2 = String:: from(“hello”);
s1.push_str(“, rust”);
//iterating over the characters in a string
for c in s1.chars() {
println!(“{:?}”, c)
### Vector
Like `array`, vector `Vec` can store a single type of values next to each other. Unlike `array`, `Vec` is allocated in the heap and doesn’t need to have a fixed length at compile time.
**you can find the entire list of methods for `Vec` and any data type in rust from their [documentation](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts).**
#### operating a vector
// create a empty vector
let mut v: Vec
// push in and pop off value from vector
v.push(5);
v.push(6);
// create a vector with initialized values
let v = vec![5,5,5,5];
let v = vec![5;4];
#### iterating over the values in a vector
let v = vec![100, 32, 57];
for i in &v {
println!(“{}”, i);
if you want to change the values as well, we take the mutable reference:
let mut v = vec![100, 32, 57];
for i in &mut v {
#### slicing a vector
for i in &mut v[5..8] {
#### Generics and Polymorphism
The std library defines `Vec
– `Vec
– `Vec<&str>` is a vector of string slices
This can also be used in functions:
fn id
In this definition, `
### HashMap
The last of our common collections is the hash map. Hash maps store their data on the heap.
The type `HashMap
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
// insert values
scores.insert(String::from(“Blue”), 10);
scores.insert(String::from(“Yellow”), 50);
// get values
for (key, value) in &scores {
println!(“{}: {}”, key, value);
//overwrite
scores.insert(String::from(“Blue”), 25);
//get or insert
let blue = scores.entry(String::from(“Blue”)).or_insert(50);
*blue *= 2;
or, you can initialize your `HashMap` from iterators and the `collect` method on a vector of tuples.
use std::collections::HashMap;
let teams = vec![String::from(“Blue”), String::from(“Yellow”)];
let initial_scores = vec![10, 50];
let mut scores: HashMap<_, _> =
teams.into_iter() // turn this variable into iterator
.zip(initial_scores.into_iter()) // zip it with another iterator
.collect(); // collect the zipped iterator as a vector of tuples.
## The Stack and the Heap
Both the stack and the heap are parts of memory that are available to your code to use at runtime, but they are structured in different ways.
### The stack
– The stack stores values in the order *last in, first out*.
– **All data stored on the stack must have a known, fixed size**.
– Adding data is called *pushing onto* the stack. Removing data is called *popping off* the stack.
– When your code calls a function, the values passed into the function (including, potentially, pointers to data on the heap) and the function’s local variables get pushed onto the stack. When the function is over, those values get popped off the stack.
### The heap
– when you put data on the heap, you request a certain amount of space.
– The memory allocator finds an empty spot in the heap that is big enough, marks it as being in use (by ownership), and returns a *pointer*, which is the address of that location.
– This process is called allocating on the heap and is sometimes abbreviated as just allocating.
– you can store the pointer on the stack, but when you want the actual data, you must follow the pointer.
### The Stack VS The Heap
– Pushing to the stack is faster than allocating on the heap
– because the allocator never has to search for a place to store new data; that location is always at the top of the stack.
– Accessing data on the stack is faster than accessing data in the heap
– because you have to follow a pointer to get there.
– A processor can do its job better if it works on data that is close to other data (as it is on the stack) rather than farther away (as it can be on the heap).
## Ownership
Rust’s heap memory is managed through a system of *ownership* with a set of rules that the compiler checks at compile time. **None of the ownership features slow down your program while it’s running.**
### Ownership Rules
First, let’s take a look at the ownership rules. Keep these rules in mind as we work through the examples that illustrate them:
– 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 (when the *lifetime* is over), the value will be dropped.
– Each piece of memory has its owner. No data race!
– When the owner gets dropped, the piece of memory will be freed. No dangling pointers!
#### Heap memory
Heap memory is managed by ownership.
let s1 = String::from(“hello”);
let s2 = s1; //x moved to y
println!(“{}”, s1); // error, `s1` doesn’t own the value anymore.
} // s1, s2 are droped at this point.
when assigning `s1` to `s2`, we pass the ownership of the stored value, “hello”, from `s1` to `s2`. In other words, we *move* the value from `s1` to `s2`. So `s1` cannot access the data anymore.
If we really want to keep `s1` and `s2` at the same time, we can `clone` it explicitly:
let s1 = String::from(“hello”);
let s2 = s1.clone();
println!(“s1 = {}, s2 = {}”, s1, s2);
When the scope, marked by `{}` is over, rust calls `drop()` function automatically to drop `s1` and `s2`. In other words, delete these two variables and clear the memory they use.
#### stack memory
On the contrary, if we do this for values on the stack, for example
let x: i32 = 5;
let y = x;
println!(“x = {}, y = {}”, x, y); //works
This is OK because x has a primitive type, `i32`, whose length is known at compile-time and is stored on the stack. So, the Rust compiler will *copy* the value of `x` to `y`, so both `x` and `y` have the same value `5i32` (but are stored in a different place on the stack).
This is because `i32` has the `Copy` trait. If a type implements the `Copy` trait, the value of the type will be copied after the assignment.
#### Trait
A Trait is a way of saying that a type has a particular property
– `Copy`: objects with this trait do not transfer ownership on assignment. Instead, the assignment copies all of the object data
– `Move`: objects with this trait do transfer ownership on assignment usually so that not all of the data need be copied.
Another way of using traits: to indicate functions that a type is must implement (more later)
– Like Java interfaces
– Example: Deref built-in trait indicates that an object can be dereferenced via `*` op; compiler calls object’s `deref()` method
### Ownership Transfer in Function Calls
fn main() {
let s = String::from(“hello”); // s comes into scope, in the heap
takes_ownership(s); // s’s value moves into the function…
// … and so is no longer valid here
let x = 5; // x comes into scope, on the stack
makes_copy(x); // x would move into the function,
// but i32 is Copy, so it’s okay to still
// use x afterward
} // Here, x goes out of scope, then s. But because s’s value was moved, nothing
// special happens.
fn takes_ownership(some_string: String) { // some_string comes into scope
println!(“{}”, some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
// memory is freed.
fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!(“{}”, some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.
On a call, ownership passes from:
– argument to called function’s parameter
– returned value to caller’s receiver
## References and Borrowing
### Borrowing avoids transferring ownership
If we don’t want a function to take ownership of our data, we can pass a reference to the function. Creating an explicit, non-owning pointer by making a reference is called `borrowing` in rust. Done with `&` ampersand operator. The opposite of referencing by using `&` is dereferencing, which is accomplished with the dereference operator, `*`.
fn main() {
let s1 = String::from(“hello”);
let len = calculate_length(&s1); // &s1 has type &String
println!(“The length of ‘{}’ is {}.”, s1, len);
fn calculate_length(s: &String) -> usize {
### mutable reference
`&` alone doesn’t give us the permission to modify the data. Remember that in Rust, everything is by default immutable. To make a mutable reference, we need to specify `&mut` when making a reference.
fn main() {
let mut s = String::from(“hello”);
change(&mut s);
fn change(some_string: &mut String) {
some_string.push_str(“, world”);
**REMEMBER:** Mutable references have one big restriction: you can have only one mutable reference to a particular piece of data in a particular scope.
The following code will fail
fn main() {
let mut s = String::from(“hello”);
let r1 = &mut s;
let r2 = &mut s; // cannot have multiple mutable reference!
println!(“{}, {}”, r1, r2);
The benefit of having this restriction is that **Rust can prevent [data races](https://docs.oracle.com/cd/E19205-01/820-0619/geojs/index.html#:~:text=A%20data%20race%20occurs%20when,their%20accesses%20to%20that%20memory.) at compile-time**.
**Each piece of data in memory can have either one mutable reference or multiple immutable references.**
fn main() {
let mut s = String::from(“hello”);
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!(“{}, {}, and {}”, r1, r2, r3);
### No Dangling References
In languages with pointers, it’s easy to erroneously create a *dangling pointer*, a pointer 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 dangling pointer will NEVER happen** because Rust compiler will make sure that if you have a reference to some data, the data will not go out of scope before the reference to the data does.
fn main() {
// This function cannot return because
// the variable s is dropped at the end of the function.
let reference_to_nothing = dangle();
fn dangle() -> &String {
let s = String::from(“hello”);
Instead, you need to return an actual string.
fn main() {
let string = no_dangle();
fn no_dangle() -> String {
let s = String::from(“hello”);
Another data type that does not have ownership is the slice. Slices let you reference a contiguous sequence of elements in a collection rather than the whole collection.
### String slices
When passing the pointer to a string into a function, it is better to pass a string slice (`&str`) rather than a reference to the string (`&String`), so as to [avoid layers of indirection](https://rust-unofficial.github.io/patterns/idioms/coercion-arguments.html).
fn main() {
let s = String::from(“hello world”);
let hello = &s[0..5];
let world = &s[6..11];
– `s` owns the string.
– `world` is a reference pointing to the second word of the string.
### array slices
#![allow(unused)]
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
assert_eq!(slice, &[2, 3]);
### vector slices
fn main() {
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!(“The third element is {}”, third);
match v.get(2) {
Some(third) => println!(“The third element is {}”, third),
None => println!(“There is no third element.”),
## Glance at `struct` and `enum`
### Structs
*Struct* allows you to group multiple pieces of data with different types together. You can name each piece of data and query them by name. Each piece of data and its name is called a field. More at [TRPL](https://doc.rust-lang.org/book/ch05-00-structs.html) and [ Example](https://doc.rust-lang.org/rust-by-example/custom_types/structs.html).
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
let user1 = User {
username: String::from(“someusername123”),
active: true,
sign_in_count: 1,
Functions defined for a `struct` are called the methods of the struct. This can be done using the `impl` keyword.
impl User {
fn get_username(&self) -> &str {
&self.username[..]
If the variants of a variable are well-defined, and each instance of the variable can take one of the variances, `enum` is your friend. For example, the possible kinds of IP addresses can be either V4 or V6. In this case, we can define the variable as
enum IpAddrKind {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
Enums can take value, for example
enum IpAddr {
V4(String),
V6(String),
let home = IpAddr::V4(String::from(“127.0.0.1”));
let loopback = IpAddr::V6(String::from(“::1”));
We can have a wide variety of types embedded in an `enum`’s variants.
enum Message {
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
You can implement methods for Enums as well.
impl IpAddr {
fn print_summary(&self) {
match self {
Self::V4(addr) => {
println!(“{IpV4 address: {}}”, addr)
Self::V6(addr) => {
println!(“{IpV6 address: {}}”, addr)
If we want to do something for a special variant:
if let IpAddr::V4(addr) = home {
println!(“Get an IpV4 address: {}”, addr)
More at [TRPL](https://doc.rust-lang.org/book/ch06-00-enums.html) and [ Example](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html).
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com