Technologieaustausch

Tag 2 Üben Sie die Rust-Sprache mit Rascheln – Bewegungssemantik

2024-07-12

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

Hallo zusammen

Heute habe ich das 2024 Autonomous Driving OS Development Training Camp – die vierte Phase des Elementary Camp – Tutorial abgeschlossen

Tag 2 Üben Sie die Rust-Sprache mit Rascheln – Bewegungssemantik

alt
alt
alt

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

Wenn ich den Code übermittle, erhalte ich die Meldung, dass ich keine Berechtigung habe. Wie komme ich raus?

alt
alt

Aktion

alt

Konfiguration der Referenzentwicklungsumgebung

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

mein Thema

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

Erstellen Sie einen SSH-Schlüssel, um den Github-Code per SSH zu klonen. Verwenden Sie in der Linux-Umgebung den Befehl ssh-keygen -t rsa -b 4096 -C „Ihre Mailbox“, um einen SSH-Schlüssel zu erstellen. Drücken Sie einfach die Eingabetaste, um alle folgenden Optionen anzuzeigen. Dann verwenden Sie cat ~/.ssh/id_rsa.pub

Primitive Typen

Rust hat einige grundlegende Typen, die direkt im Compiler implementiert sind. In diesem Abschnitt gehen wir die wichtigsten durch.

Weitere Informationen

alt

Mit dem Slice-Typ „Slices“ können Sie auf eine zusammenhängende Folge von Elementen in einer Sammlung verweisen, statt auf die gesamte Sammlung.

Ein Slice ist eine Art Referenz und besitzt daher keinen Besitzanspruch.

alt
alt

In Rust ist ein Slice eine Ansicht, die auf eine Folge zusammenhängender Elemente aus einem Array oder einem anderen Slice verweist. Um ein Slice zu erstellen, müssen Sie die Start- und Endposition des Slice angeben (ohne den Index der Endposition).im Arraya rein, wenn du rein willst[2, 3, 4] Für dieses Segment müssen Sie bei Index 1 beginnen und bei Index 4 enden (Index 4 nicht eingerechnet).

So korrigieren Sie Ihren Testcode:

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

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

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

In diesem Beispiel,&a[1..4] Erstellt ein Array ausa Der Slice beginnt bei Index 1 und endet bei Index 3, da Rusts Slicing-Syntax ein links-geschlossenes und rechts-offenes Intervall ist (einschließlich des Anfangs, aber nicht des Endes).Alsonice_slice Es enthält das Arraya Mitte[2, 3, 4]

Geben Sie in der Befehlszeile „rustlings watch“ ein, um das Schleifenexperiment zum Bearbeiten des Codes zu starten

alt

Eigentum verstehen

Eigentum ist das einzigartigste Merkmal von Rust und hat tiefgreifende Auswirkungen auf den Rest der Sprache.

Es ermöglicht Rust, Speichersicherheitsgarantien zu geben, ohne dass ein Garbage Collector erforderlich ist.

Was ist Eigentum?

alt

Eigentumsregeln Werfen wir zunächst einen Blick auf die Eigentumsregeln. Behalten Sie diese Regeln im Hinterkopf, während wir die Beispiele durchgehen, die sie veranschaulichen:

  • Jeder Wert in Rust hat einen Besitzer.
  • Es kann immer nur einen Eigentümer geben.
  • Wenn der Besitzer den Gültigkeitsbereich verlässt, wird der Wert gelöscht.

alt
alt

Das Codebeispiel selbst ist korrekt und demonstriert die Konzepte von Eigentum und Kopieren in Rust. Wenn Sie jedoch sicherstellen möchten, dass Ihr Code kompiliert und ausgeführt wird, und Sie einige mögliche Verbesserungen oder Korrekturen vornehmen möchten, finden Sie hier einige Vorschläge:

  1. stellen Sie sicher takes_ownership Undmakes_copy Funktion inmain zuvor definierte Funktion, da Funktionen in Rust zuerst deklariert und dann verwendet werden müssen.

  2. Notizen hinzufügen, erkläre warum s existierentakes_ownership kann nach dem Aufruf nicht mehr verwendet werden, undx existierenmakes_copy Es kann auch nach dem Aufruf weiterhin genutzt werden.

  3. Hinzufügen zu main Funktionsrückgabetyp, obwohl dies in einfachen Programmen nicht notwendig ist, ist es eine gute Programmierpraxis.

  4. Verwendung der Formatierungstools von Rust,Zum Beispiel rustfmt, um den Code sauber zu halten.

Hier ist der geänderte Code basierend auf den obigen Vorschlägen:

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

In dieser überarbeiteten Version haben wir hinzugefügt main Funktionsrückgabetyp(),das heisst main Die Funktion gibt keinen Wert zurück.Außerdem werden Kommentare hinzugefügt, um zu erklären, warums existierentakes_ownership kann nach dem Aufruf nicht mehr verwendet werden, undx existierenmakes_copy Nach Anruf noch gültig.Darüber hinaus zeigen wir Versuche dazutakes_ownership Verwendung nach Anrufs Beispiele, die Kompilierungsfehler verursachen würden, undx existierenmakes_copy Beispiele, die auch nach dem Aufruf noch verwendet werden können.

Vektoren

Vektoren sind eine der am häufigsten verwendeten Rust-Datenstrukturen.

In anderen Programmiersprachen würden sie einfach Arrays genannt, aber da Rust auf einer etwas niedrigeren Ebene operiert, wird ein Array in Rust auf dem Stack gespeichert (was bedeutet, dass es nicht wachsen oder schrumpfen kann und die Größe zur Kompilierzeit bekannt sein muss) und ein Vektor wird auf dem Heap gespeichert (wo diese Einschränkungen nicht gelten).

Vektoren werden in diesem Buch erst in einem späteren Kapitel behandelt, aber wir denken, dass sie nützlich genug sind, um etwas früher darüber zu sprechen. Über die andere nützliche Datenstruktur, Hash-Maps, werden wir später sprechen.

Weitere Informationen

alt vec_map Die Funktion besteht darin, a zu akzeptierenVec<i32> Typ (d. h. ein Vektor, der eine 32-Bit-Ganzzahl enthält) und gibt dann einen neuen zurückVec<i32>, jedes Element in diesem neuen Vektor ist doppelt so groß wie das entsprechende Element im ursprünglichen Vektor.

Speziell,vec_map in Funktionmap Undcollect Die Methode funktioniert folgendermaßen:

  1. **v.iter()**: Diese Methode erstellt einen durchlaufenden Iterator v jedes Element des Vektors.iter Der von der Methode zurückgegebene Iterator stellt eine unveränderliche Referenz auf die Elemente im Vektor bereit.

  2. **.map(|element| { element * 2 })**:map Die Methode akzeptiert einen Abschluss (anonyme Funktion), in diesem Fall akzeptiert der Abschluss einen Parameterelement (ein unveränderlicher Verweis auf ein Element im Vektor) und gibt dann den Wert dieses Elements multipliziert mit 2 zurück.Operationen innerhalb von Schließungenelement * 2 Tatsächlich dereferenzieren Sie die Daten, auf die die Referenz verweist (via* Operator) und führen Multiplikationsoperationen durch.

  3. **.collect()**:map Die Methode gibt einen Lazy-Iterator zurück, der die Operationen im Abschluss nacheinander anwendet, die Ergebnisse jedoch nicht sofort sammelt.collect Die Methode wird aufgerufen, um diese zu übergebenmap Verarbeitete Elemente werden zu einem neuen zusammengefasstVec<i32> im Vektor.

verwenden map Undcollect Der Vorteil besteht darin, dass Sie damit Operationen für jedes Element in der Sammlung auf deklarative Weise ausführen und die Ergebnisse in einer neuen Sammlung sammeln können, ohne explizite Schleifen schreiben zu müssen. Auf diese Weise ist der Code prägnanter und konsistenter mit dem Rust-Stil.

Hier erfahren Sie, wie Sie es verwenden vec_map Funktionsbeispiel:

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

In diesem Beispiel,vec_map übernehmenoriginal_vec Referenz und erstellt einen neuen Vektordoubled_vec, das den doppelten Wert jedes Elements des Originalvektors enthält und ausgibt.

Semantik verschieben

Diese Übungen sind adaptiert von Abonnieren'S Rust-Tutorial -- Danke, Felix!!!

Weitere Informationen

Für diesen Abschnitt sind die Buchlinks besonders wichtig.

move_semantics1.rs

alt

move_semantics2.rs

alt
alt

// move_semantics5.rs

alt

In Rust gibt es eine Regel für veränderliche Referenzen: Sie können jederzeit eine beliebige Anzahl unveränderlicher Referenzen oder höchstens eine veränderliche Referenz haben. Diese Regel gewährleistet die Speichersicherheit und verhindert Datenrennen.

Im bereitgestellten Code gibt es ein Problem:main Die Funktion hat versucht, zwei veränderbare Referenzen zu erstelleny Undz, beide zeigen auf dieselbe Variable x . Dies ist nicht zulässig, da die Ausleihregeln von Rust nicht mehrere veränderbare Verweise auf dieselbe Variable im selben Bereich zulassen. Aus diesem Grund lässt sich der Code nicht kompilieren.

Um dieses Problem zu beheben, müssen Sie sicherstellen, dass jeweils nur eine veränderbare Referenz vorhanden ist. Hier ist der feste Code:

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

In dieser festen Version stellen wir sicher, dass immer nur eine veränderbare Referenz vorhanden ist, indem wir einen inneren Bereich erstellen.Zuerst haben wir erstellty Bereich, eine Operation wird darauf ausgeführt, und dann endet dieser Bereich.y Nicht länger gültig.Danach haben wir erstelltz Spielraum, wieder fürx Operationen durchführen. Dies folgt den Ausleihregeln von Rust, sodass der Code wie erwartet kompiliert und ausgeführt wird.

alt
alt
alt

Diese Kompilierungsfehlermeldung weist auf ein Lebenszyklusproblem im Rust-Code hin.Das Problem iststring_uppercase Bei der Implementierung der Funktion wird versucht, einer Referenz einen temporären Wert zuzuweisen, was nicht zulässig ist.

Hier ist die Codezeile, die schief geht:

data = &data.to_uppercase();
  • 1

In dieser Codezeiledata.to_uppercase() Erschuf einString Eine neue Instanz vondata geliehen.Versuchen Sie jedoch, diesem temporären Wert eine Referenz zuzuweisendata ist illegal, da der temporäre Wert sofort am Ende der Anweisung zerstört wird, unddata Erfordert eine längere Lebensdauer als temporäre Werte.

Um dieses Problem zu beheben, müssen wir Änderungen vornehmen string_uppercase Eine Funktion, die eine veränderliche Referenz akzeptiert und direkt für die Daten aufgerufen wird, auf die die Referenz verweistmake_uppercase() Methode, anstatt eine Neuzuweisung zu versuchen. Hier ist der feste Code:

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

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

In dieser festen Version haben wir entfernt mut data: &String Mittemut,Weil data Bereits eine veränderliche Referenz.Dann wir direktdata ruf anmake_uppercase(), daher wird es kein Problem mit temporären Werten geben.make_uppercase() Die Methode wird geändertdata Bezugspunkte zuString Instanz, ohne eine neue zu erstellenString

Auch,main Auch der Aufruf innerhalb der Funktion muss entsprechend aktualisiert werden, um eine veränderbare Referenz zu übergeben:

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

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

Jetzt,string_uppercase Funktion akzeptiertdata eine veränderliche Referenz, ändern Sie sie direkt undmain in Funktiondata ist veränderlich und kann seinstring_uppercase geändert. Auf diese Weise wird der Code kompiliert und funktioniert wie erwartet.

alt
alt
alt
/**
【昵称】小王同学
【坐标】山东
【自我介绍】
    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

Dieser Artikel wurde geschrieben von AbonnierenMultiplattform-Veröffentlichung