Rust 内存模型

Rust 中的数据类型

类型 描述 值(样例)
i8, i16, i32, i64,
u8, u16, u32, u64
确定字节的有符号或者无符号整数 42,
-5i8, 0x400u16, 0o100i16,
20_922_789_888_000u64,
b’*’ (u8 byte literal)
isize, usize 与机器地址长度相同(32 or 64 bits)的有符号或者无符号整数 137,
-0b0101_0010isize,
0xffff_fc00usize
f32, f64 IEEE 浮点数, 单精度和双精度 1.61803, 3.14f32, 6.0221e23f64
bool Boolean true, false
char Unicode character, 32 bits wide ‘*’, ‘\n’, ‘字’, ‘\x7f’, ‘\u{CA0}’
(char, u8, i32) 元组: 允许类型混合 (‘%’, 0x7f, -1)
() “unit” (empty) tuple ()
struct S { x: f32, y: f32 } Named-field struct S { x: 120.0, y: 209.0 }
struct T(i32, char); Tuple-like struct T(120, ‘X’)
struct E; Unit-like struct; has no fields E
enum Attend{ OnTime,Late(u32) } Enumeration, algebraic data type Attend::Late(5), Attend::OnTime
Box<Attend> Box: owning pointer to value in heap Box::new(Late(15))
&i32, &mut i32 Shared and mutable references: nonowning pointers that must not outlive their referent &s.y, &mut v
String UTF-8 string, dynamically sized “ラーメン: ramen”.to_string()
&str Reference to str: nonowning pointer to UTF-8 text “そば: soba”, &s[0..12]
[f64; 4], [u8; 256] Array, fixed length; elements all of same type [1.0, 0.0, 0.0, 1.0], [b’ ‘; 256]
Vec Vector, varying length; elements all of same type vec![0.367, 2.718, 7.389]
&[u8], &mut [u8] Reference to slice: reference to a portion of an array or vector, comprising pointer and length &v[10..20], &mut a[..]
Option<&str> Optional value: either None (absent) or Some(v) (present, with value v) Some(“Dr.”), None
Result Result of operation that may fail: either a success value Ok(v), or an error Err(e) Ok(4096), Err(Error::last_os_error())
&Any, &mut Read Trait object: reference to any value that implements a given set of methods value as &Any, &mut file as &mut Read
fn(&str, usize) -> isize Pointer to function i32::saturating_add
(Closure types have no written form) Closure |a, b| { a*a + b*b }

基本类型

1
2
3
4
5
let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707]
let v: [f64; 4] = [0.0, 0.707, 1.0, 0.707]

let sv: &[f64] = &v;
let sa: &[f64] = &a;

向量和数组在内存中的位置是不一样的

1
2
3
4
let noodles = "noodles".to_string();
let oodles = "noodles".to_string();
let poodles = "޵_޵";

不同类型的字符串在内存中的形式

所有权和所有权转移

所有权

C++ 中的字符串

1
std::string s = "frayed knot";

Rust 中的字符串

1
2
3
4
5
6
7
8
fn print_padovan() {
let mut padovan = vec![1,1,1]; // allocated here
for i in 3..10 {
let next = padovan[i-3] + padovan[i-2];
padovan.push(next);
}
println!("P(1..10) = {:?}", padovan);
} // dropped here

Box

1
2
3
4
5
{
let point = Box::new((0.625, 0.5)); // point allocated here
let label = format!("{:?}", point); // label allocated here
assert_eq!(label, "(0.625, 0.5)");
} // both dropped here

树形的所有权

1
2
3
4
5
6
7
8
struct Person { name: String, birth: i32 }
let mut composers = Vec::new();
composers.push(Person { name: "Palestrina".to_string(), birth: 1525 });
composers.push(Person { name: "Dowland".to_string(), birth: 1563 });
composers.push(Person { name: "Lully".to_string(), birth: 1632 });
for composer in &composers {
println!("{}, born {}", composer.name, composer.birth);
}

所有权转移

指针赋值

  • Python

    1
    2
    3
    s = ['udon', 'ramen', 'soba']
    t = s
    u = s

    Python 怎么在内存中存储string数组数据
    当进行赋值时, 会出现什么

    这三个指针将会指向同一块内存地址

  • C++

    1
    2
    3
    4
    using namespace std;
    vector<string> s = { "udon", "ramen", "soba" };
    vector<string> t = s;
    vector<string> u = s;

    C++ 会将这些数据都进行拷贝

  • Rust

    1
    2
    3
    let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
    let t = s;
    let u = s;

    Rust 中, 赋值会导致所有权的转移


    因为所有权转移了, s 不再持有数据, 编译器将在 let u = s; 抛出错误.

    1
    2
    3
    4
    5
    6
    7
    8
    error[E0382]: use of moved value: `s`
    --> ownership_double_move.rs:9:9
    |
    8 | let t = s;
    | - value moved here
    9 | let u = s;
    | ^ value used here after move
    |

    如果你是需要副本, 可以显式的进行复制

    1
    2
    3
    let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
    let t = s.clone();
    let u = s.clone();

所有权在类型内外之间转移

假设你现在运行这些代码

1
2
3
let mut noodles = vec!["udon".to_string()];
let soba = "soba".to_string();
let last;

在内存中的形式
1
noodles.push(soba)

当你执行上面的代码之后, soba 的所有权将会转移到 noodles 中.

1
last = noodles.pop().unwrap();

而当你执行以上的代码后, 字符串的所有权又将交给 last

移动所有权的例外 — 可复制的类型

1
2
3
4
let str1 = "somnambulance".to_string();
let str2 = str1;
let num1: i32 = 36;
let num2 = num1;

共享的所有权

1
2
3
4
5
use std::rc::Rc;
// Rust can infer all these types; written out for clarity
let s: Rc<String> = Rc::new("shirataki".to_string());
let t: Rc<String> = s.clone();
let u: Rc<String> = s.clone();


但是共享所有权可能会带来循环引用的问题, 引起内存泄漏

引用

可以使一个变量改变自己的指向

1
2
3
4
5
let x = 10;
let y = 20;
let mut r = &x;
if b { r = &y; }
assert!(*r == 10 || *r == 20);

引用可以指向引用

1
2
3
4
5
struct Point { x: i32, y: i32 }
let point = Point { x: 1000, y: 729 };
let r: &Point = &point;
let rr: &&Point = &r;
let rrr: &&&Point = &rr;

引用的生命周期不能超过被引用数据的生命周期



1
2
3
4
let v = vec![4, 8, 19, 27, 34, 10];
let r = &v;
let aside = v; // move vector to aside
r[0]; // bad: uses `v`, which is now uninitialized


1
2
3
4
5
6
7
error[E0505]: cannot move out of `v` because it is borrowed
--> references_sharing_vs_mutation_1.rs:10:9
|
9 | let r = &v;
| - borrow of `v` occurs here
10 | let aside = v; // move vector to aside
| ^^^^^ move out of `v` occurs here

1
2
3
4
5
6
let v = vec![4, 8, 19, 27, 34, 10];
{
let r = &v;
r[0]; // ok: vector is still there
}
let aside = v;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn extend(vec: &mut Vec<f64>, slice: &[f64]) {
for elt in slice {
vec.push(*elt);
}
}

let mut wave = Vec::new();
let head = vec![0.0, 1.0];
let tail = [0.0, -1.0];

// 可以使用其他数组进行扩展
extend(&mut wave, &head); // extend wave with another vector
extend(&mut wave, &tail); // extend wave with an array
assert_eq!(wave, vec![0.0, 1.0, 0.0, -1.0]);

// 但是可以自己扩展自己吗?
extend(&mut wave, &wave);
assert_eq!(wave, vec![0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0]);


答案是不行, 因为当数组的长度扩展之后, 应该是指向新分配的内存, 但是旧的引用还是指向旧的内存地址. 这不仅仅是Rust 会出现的问题.
不过 Rust 会在编译时阻止你这样做

1
2
3
4
5
6
7
8
error[E0502]: cannot borrow `wave` as immutable because it is also borrowed as mutable
--> references_sharing_vs_mutation_2.rs:9:24
|
9 | extend(&mut wave, &wave);
| ---- ^^^^- mutable borrow ends here
| | |
| | immutable borrow occurs here
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Paring these principles down to the simplest possible examples:

let mut x = 10;
let r1 = &x;
let r2 = &x; // ok: multiple shared borrows permitted
x += 10; // error: cannot assign to `x` because it is borrowed
let m = &mut x; // error: cannot borrow `x` as mutable because it is
// also borrowed as immutable


let mut y = 20;
let m1 = &mut y;
let m2 = &mut y; // error: cannot borrow as mutable more than once
let z = y; // error: cannot use `y` because it was mutably borrowed

// It is OK to reborrow a shared reference from a shared reference:

let mut w = (107, 109);
let r = &w;
let r0 = &r.0; // ok: reborrowing shared as shared
let m1 = &mut r.1; // error: can't reborrow shared as mutable

// You can reborrow from a mutable reference:

let mut v = (136, 139);
let m = &mut v;
let m0 = &mut m.0; // ok: reborrowing mutable from mutable
*m0 = 137;
let r1 = &m.1; // ok: reborrowing shared from mutable,
// and doesn't overlap with m0
v.1; // error: access through other paths still forbidden

Trait Object

Unsized Type

Closure

Closure Type

• Fn is the family of closures and functions that you can call multiple times without restriction. This highest category also includes all fn functions.
• FnMut is the family of closures that can be called multiple times if the closure itself is declared mut.
• FnOnce is the family of closures that can be called once, if the caller owns the closure.

VecDeque

HashMap

BTreeMap

Self Referencing


Rust 内存组织像一棵树一样

Author: Sean
Link: https://blog.whileaway.io/posts/8930f342/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.