技術共有

Day2 カサカサ音を使った Rust 言語の練習 - 移動セマンティクス

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

こんにちは、みんな

本日、2024年自動運転OS開発トレーニングキャンプ~初級キャンプ第4期~チュートリアルを修了しました。

Day2 カサカサ音を使った Rust 言語の練習 - 移動セマンティクス

代替
代替
代替

https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html

コードを送信すると、許可がないというメッセージが表示されます。どうすれば抜け出せますか?

代替
代替

行為

代替

参考開発環境構成

https://rcore-os.cn/arceos-tutorial-book/ch01-02.html

私の話題

https://github.com/cicvedu/rustlings-semester-4-watchpoints

ssh キーを作成して、ssh 経由で github コードのクローンを作成します。 Linux 環境では、ssh-keygen -t rsa -b 4096 -C "your mailbox" コマンドを使用して、次のすべてのオプションで Enter を押すだけです。 次に、cat ~/.ssh/id_rsa.pub を使用します。

プリミティブ型

Rust には、コンパイラに直接実装されている基本的な型がいくつかあります。このセクションでは、最も重要なものについて説明します。

さらに詳しい情報

代替

スライス タイプ スライスを使用すると、コレクション全体ではなく、コレクション内の連続した要素のシーケンスを参照できます。

スライスは参照の一種なので、所有権はありません。

代替
代替

Rust では、スライスは配列または別のスライスから連続した要素のシーケンスを参照するビューです。スライスを作成するには、スライスの開始位置と終了位置を指定する必要があります (終了位置のインデックスは含まれません)。配列内a 入手したい場合は[2, 3, 4] このスライスの場合、インデックス 1 で開始し、インデックス 4 で終了する必要があります (インデックス 4 は除く)。

テストコードを修正する方法は次のとおりです。

#[test]
fn slice_out_of_array() {
    let a = [12345];

    // 从索引 1 开始,到索引 4 结束的切片
    let nice_slice = &a[1..4];

    assert_eq!([234], nice_slice);
}
  • 1

この例では、&a[1..4] から配列を作成しましたa Rust のスライス構文は左が閉じて右が開いた間隔 (開始は含みますが、終了は含みません) であるため、スライスはインデックス 1 で開始し、インデックス 3 で終了します。それでnice_slice 配列が含まれていますa 真ん中[2, 3, 4]

コマンドラインに「rustlings watch」と入力して、コード編集のループ実験を開始します。

代替

所有権を理解する

所有権は Rust の最もユニークな機能であり、言語の残りの部分に深い影響を与えます。

Rustはガベージコレクションを必要とせずにメモリの安全性を保証できます。

所有権とは何ですか?

代替

所有権のルール まず、所有権のルールを見てみましょう。これらのルールを念頭に置いて、例を見ながら説明していきます。

  • Rust の各値には所有者がいます。
  • 一度に所有者になれるのは 1 人だけです。
  • 所有者がスコープ外になると、値は削除されます

代替
代替

コードサンプル自体は正しく、Rust における所有権とコピーの概念を示しています。ただし、コードがコンパイルされて実行されることを確認し、考えられる改善や修正に対処したい場合は、次のような提案があります。

  1. 確認する takes_ownership そしてmakes_copy で機能するmain 前に定義された関数なぜなら、Rust の関数は最初に宣言してから使用する必要があるからです。

  2. メモを追加する、 理由を説明 s 存在するtakes_ownership 呼び出し後は使用できません。x 存在するmakes_copy 呼び出し後も使用できます。

  3. に追加 main 関数の戻り値の型、これは単純なプログラムでは必要ありませんが、プログラミングの良い練習になります。

  4. Rustの書式設定ツールの使用、例えば rustfmt、コードをきれいに保つため。

上記の提案に基づいて変更されたコードは次のとおりです。

fn takes_ownership(some_string: String) { // 函数定义在 main 之前
    println!("{}", some_string);
// some_string 的作用域结束,调用 Drop

fn makes_copy(some_integer: i32) { // 函数定义在 main 之前
    println!("{}", some_integer);
// some_integer 的作用域结束,没有调用 Drop,因为 i32 是 Copy 类型

fn main() -> () { // 明确 main 函数的返回类型为 ()
    let s = String::from("hello");  // s 拥有 "hello" 的所有权

    takes_ownership(s);             // s 的值移动到 takes_ownership 函数

    // 下面的代码尝试使用 s 将会导致编译错误,因为 s 的值已经移动
    // let len = s.len(); // 错误:s 已移动

    let x = 5;                      // x 包含值 5

    makes_copy(x);                  // x 的值被复制到 makes_copy 函数
                                    // x 仍然有效,因为 i32 是 Copy 类型

    // x 仍然可以使用
    println!("x is {}", x);
}
  • 1

今回の改訂版では以下のことを追加しました main 関数の戻り値の型()、これはつまり main 関数は値を返しません。また、その理由を説明するコメントも追加されていますs 存在するtakes_ownership 呼び出し後は使用できません。x 存在するmakes_copy 電話後も有効です。さらに、次のような試みも示します。takes_ownership 電話してから利用するs コンパイルエラーが発生する例と、x 存在するmakes_copy 電話をかけた後も使用できる例。

ベクトル

ベクターは、最もよく使用される Rust データ構造の 1 つです。

他のプログラミング言語では、単に配列と呼ばれますが、Rust は少し低いレベルで動作するため、Rust の配列はスタックに格納され (つまり、拡大または縮小できず、コンパイル時にサイズがわかっている必要があります)、ベクターはヒープに格納されます (これらの制限は適用されません)。

ベクトルは、この本の中では後の章で取り上げますが、役に立つので、もう少し前に説明する価値があると思います。他の役に立つデータ構造であるハッシュ マップについては、後で説明します。

さらに詳しい情報

代替 vec_map 機能は、Vec<i32> type (つまり、32 ビット整数を含むベクトル) を返し、新しい値を返します。Vec<i32>、この新しいベクトルの各要素のサイズは、元のベクトルの対応する要素の 2 倍になります。

具体的には、vec_map 機能中map そしてcollect このメソッドは次のように機能します。

  1. **v.iter()**: このメソッドは、以下を横断する反復子を作成します。 v ベクトルの各要素。iter メソッドによって返される反復子は、ベクトル内の要素への不変の参照を提供します。

  2. **.map(|element| { element * 2 })**:map メソッドはクロージャ (匿名関数) を受け入れます。この場合、クロージャは 1 つのパラメータを受け入れます。element (ベクトル内の要素への不変参照)、この要素の値を 2 で乗算した値を返します。クロージャ内の操作element * 2 実際、参照によって指されたデータを逆参照しています(経由)* 演算子) を使用して乗算演算を実行します。

  3. **.collect()**:map このメソッドは、クロージャ内の操作を 1 つずつ適用する遅延反復子を返しますが、結果はすぐには収集されません。collect これらを渡すためにメソッドが呼び出されますmap 処理された要素は新しいものに集められますVec<i32> ベクトルで。

使用 map そしてcollect 利点は、明示的なループを記述することなく、コレクション内の各要素に対して宣言的な方法で操作を実行し、その結果を新しいコレクションに収集できることです。こうすることで、コードがより簡潔になり、Rust スタイルとの一貫性が高まります。

使い方はこちら vec_map 機能の例:

fn main() {
    let original_vec = vec![1234];
    let doubled_vec = vec_map(&original_vec);
    println!("{:?}", doubled_vec); // 这将打印:[2, 4, 6, 8]
}
  • 1

この例では、vec_map 引き継ぐoriginal_vec 参照、新しいベクトルの作成doubled_vecこれには、元のベクトルの各要素の 2 倍の値が含まれ、出力されます。

移動セマンティクス

これらのエクササイズは、 翻訳者Rust チュートリアル -- ありがとう、フェリックス!!!

さらに詳しい情報

このセクションでは、書籍のリンクが特に重要です。

移動セマンティクス1.rs

代替

移動セマンティクス2.rs

代替
代替

// 移動セマンティクス5.rs

代替

Rust には可変参照に関するルールがあります。いつでも、不変参照はいくつでも、または可変参照は 1 つだけ持つことができます。このルールによりメモリの安全性が確保され、データ競合が防止されます。

提供されたコードには問題があります:main 関数は 2 つの可変参照を作成しようとしましたy そしてz、両方とも同じ変数を指します x 。 Rust の借用ルールでは、同じスコープ内の同じ変数への複数の変更可能な参照が許可されていないため、これは許可されません。そのため、コードはコンパイルされません。

この問題を解決するには、常に 1 つの変更可能な参照のみが存在するようにする必要があります。修正されたコードは次のとおりです。

fn main() {
    let mut x = 100;
    {
        let y = &mut x;
        *y += 100// 此时 x 的值变为 200
    } // 'y' 的作用域结束,可变引用被销毁
    {
        let z = &mut x; // 可以创建新的可变引用,因为 'y' 已经不存在了
        *z += 1000// 此时 x 的值变为 1200
    } // 'z' 的作用域结束,可变引用被销毁
    assert_eq!(x, 1200); // 断言 x 的值是 1200,这是正确的
}
  • 1

この修正バージョンでは、内部スコープを作成することにより、変更可能な参照が常に 1 つだけ存在するようにします。まず、私たちが作成したのは、 y スコープに対して操作が実行され、その後このスコープが終了します。y もう有効ではない。その後、私たちが作成したのは、z スコープ、再びx 操作を実行します。これは Rust の借用ルールに従っており、コードが期待どおりにコンパイルおよび実行されます。

代替
代替
代替

このコンパイル エラー メッセージは、Rust コードのライフサイクルの問題を示しています。問題は string_uppercase 関数の実装では、一時的な値を参照に割り当てようとしますが、これは許可されていません。

問題が発生するコード行は次のとおりです。

data = &data.to_uppercase();
  • 1

このコード行では、data.to_uppercase() を作成しましたString の新しいインスタンスdata 借りた。ただし、この一時的な値への参照を割り当ててみてください。data 一時値はステートメントの終わりですぐに破棄されるため、これは不正です。data 一時的な値よりも長い存続期間が必要です。

この問題を解決するには、変更する必要があります string_uppercase 可変参照を受け入れ、その参照が指すデータに対して直接呼び出す関数make_uppercase() 再割り当てを試みるのではなく、メソッドを使用してください。修正されたコードは次のとおりです。

fn string_uppercase(data: &mut String) {
    data.make_uppercase(); // 直接在 data 上调用 make_uppercase 方法

    println!("{}", data);
}
  • 1

この修正バージョンでは、次の点が削除されました。 mut data: &String 真ん中mut、なぜなら data すでに可変参照です。それから私たちは直接data 電話をかけるmake_uppercase(), なので、一時的な値でも問題ありません。make_uppercase() 方法が変更されますdata 参照先はString 新しいインスタンスを作成せずにインスタンスを作成するString

また、main 変更可能な参照を渡すために、関数内の呼び出しもそれに応じて更新する必要があります。

fn main() {
    let mut data = "Rust is great!".to_string();

    string_uppercase(&mut data); // 传递一个可变引用
    println!("{}", data); // 打印修改后的字符串
}
  • 1

今、string_uppercase 関数は受け入れますdata 変更可能な参照の場合は、それを直接変更し、main 機能中data 変更可能であり、可能ですstring_uppercase 変更されました。このようにして、コードはコンパイルされ、期待どおりに動作します。

代替
代替
代替
/**
【昵称】小王同学
【坐标】山东
【自我介绍】
    1. 高级后端工程师,擅长c++并发编程。
    2. 专注分布式存储,分布式数据库。
    3. 时间的践行者,立志成为一名讲师。
【我的成绩】
    1.  为了走出农村,2次高考
         一次考研失败,
         遇挫之后不知道干什么,开启被动之旅。
    2. 为了找到合适工作,   
        深入研究c++书籍和leetcode 200题目
    3. 为了提高项目能力,参与开源项目建设。
    4. 为了把简单事情说清楚/要干啥
        按照《只管去做》,《福格行为模型>>方法。
        纸上得来终觉浅,绝知此事要躬行
        做一个践行者。
【我能提供】
    1.  后端程序员的简历优化+就业辅导+职业规划
    2.  全栈工程师(c++,rust,go,python )项目开发
    3. 一年践行12本书践行记录。
【希望一起解决什么,开启破圈之旅】
    1. 交接更多朋友,抱团取暖。
        寻找意义本身就更加有意义。
    2. 无法做整个系统,聚焦一个模块
         道可道也,非恒道也 
         名可名也,非恒名也。
         无名 万物之始也
         有名 万物之母也
         别想太多,只管去做,躬身入局
     
链接我: # + v(github):watchpoints   
        #众号:后端开发成长指南
**/
  • 1

この記事を書いているのは、 インドマルチプラットフォーム出版