所有
所有権は Rust の最もユニークな機能であり、これにより Rust は GC なしでメモリの安全性を保証できます。
メモリは所有権システムを通じて管理され、コンパイラはコンパイル時に一連のルールに従ってメモリをチェックします。これらのルールのいずれかに違反すると、プログラムはコンパイルされません。実行時に、所有権システムの機能によってプログラムの速度が低下することはありません。
スタックへのプッシュは、(プッシュ時に) アロケーターが新しいデータを格納するためにメモリ領域を検索する必要がなく、その場所が常にスタックの先頭にあるため、ヒープにメモリを割り当てるよりも高速です。対照的に、ヒープ上にメモリを割り当てるには、より多くの作業が必要です。これは、アロケータがまずデータを格納するのに十分なメモリ領域を見つけてから、次の割り当てに備えていくつかのレコードを作成する必要があるためです。
ヒープ上のデータへのアクセスは、ポインターを介してアクセスする必要があるため、スタック上のデータにアクセスするよりも遅くなります。最新のプロセッサは、メモリ (キャッシュ) のジャンプが少なく、より高速になっています。
さまざまなデータ型のメモリ モデルについては後で説明します。
- 所有権が存在する理由: ヒープ (ヒープ) データの管理
1. 跟踪代码的哪些部分正在使用heap的那些数据
2. 最小化heap上的重复数据量
3. 清理heap上未使用的数据以避免空间不足
- 所有の3つのルール
1. 每个值都有一个变量,这个变量是该值的所有者
2. 每个值同时只能有一个所有者
3. 当所有者超出作用域时,该值将被删除
- String 型を例に挙げると
、これはヒープ上に割り当てられ、コンパイル時に未知の量のテキストを格納できます。
fn main(){
let mut s= String::from("Hello");
s.push_str(",World");
println!("{}",s);
}
Rust がヒープを解放する方法は次のとおりです: 特定の値について、それを所有する変数がスコープ外になると、メモリは即座に自動的にオペレーティング システムに返されます。
- 変数がデータと対話する方法: 移動
let s1=String::from("hello");
let s2=s1;
図の左側に示すように、文字列は 3 つの部分で構成されます。文字列の内容を格納するメモリへのポインタ、長さ、および容量です。このデータセットはスタックに保存されます。右側は、ヒープ上にコンテンツを保持するメモリの部分です。
s1がs2に代入されると、Stringのデータがコピーされます:
ポインタ、長さ、容量はスタック上にコピーされ、
ポインタが指すヒープ上のデータはコピーされません
変数がスコープを離れると、Rustは自動的にドロップ関数を呼び出し、変数によって使用されているヒープ メモリを解放します。
メモリの安全性を確保するために:
Rustは割り当てられたメモリをコピーしようとしませんRustは
s1を無効にします s1
がスコープ外になったときにRustは何も解放する必要がありません
シャローコピーは
コピーされたポインタ、長さ、容量をシャローコピーとして扱うことができますが、Rustはs1を無効にするため、したがって、それは移動と呼ばれます。この動作により、二次リリースの可能性が回避されます。Rust はデータのディープコピーを自動的に作成しません。
- クローン
ヒープ上に文字列データのディープ コピーを作成する場合は、クローン メソッドを使用できます。
- Copy 特性は、
完全にスタックに格納される整数などの型に使用できます。
型が Copy を実装している場合、古い変数は代入後も引き続き使用できます。
所有権と機能
意味的には、関数に値を渡すことは、変数に値を割り当てることと似ており、移動またはコピーが発生します。
fn main(){
let s = String::from("Hello World");
take_ownership(s);
let x=5;
make_copy(x);
println!(x:{
},x);
}
s は関数に入った後に関数に移動され、有効ではなくなりますが、
x はコピー特性を実装した i32 型であるため、関数に渡されるのは x のコピーであり、x は 8 行目以降も有効です。
main の終わりまでに、s と x はスコープ外になります。
- 戻り値とスコープ
所有権も変更される
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 转移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 离开作用域并被丢弃
fn gives_ownership() -> String {
// gives_ownership 会将
// 返回值移动给
// 调用它的函数
let some_string = String::from("yours"); // some_string 进入作用域.
some_string // 返回 some_string
// 并移出给调用的函数
//
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String {
// a_string 进入作用域
//
a_string // 返回 a_string 并移出给调用的函数
}
- 所有権を取得せずに関数に値を使用させるにはどうすればよいですか?
Rustには参照という機能があります
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
参照: 所有権を取得せずに値を使用できます。s は
s1 への参照、s は s1 へのポインタです。
s が関数スコープを離れた後は、所有権がないためデータは破棄されません。
-
借用
参照を関数パラメータとして使用する行為
借用した変数は不変です -
可変参照
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
可変参照には大きな制限があります。変数への可変参照がある場合、その変数への別の参照を作成することはできません。2 つの への変更可能な参照を作成しようとするコードは失敗します。コンパイル時のデータ競合を防止します。
新しいスコープを作成することで、複数の可変参照を同時に作成できるようにすることができます。
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
let r2 = &mut s;
もう 1 つの制限:
可変参照と不変参照を同時に持つことはできません。
複数の不変参照を持つことができます。
(不変参照 r1 と r2 のスコープは、println! が最後に使用された後に終了します。ここで、可変参照 r3 が作成されます。それらのスコープは重複していないため、コードはコンパイル可能です。コンパイラーはスコープが終了する前にこれを行います。使用されなくなった参照を判断する機能は、Non-Lexical Lifetimes (略して NLL) として知られています。)
- 未解決の
参照があると、Rust コンパイラがエラーをスローし、それが起こらないようにする
参考:「Rustプログラミング言語」