Technology Sharing

Day 2: Practice Rust language with rustlings - Move Semantics

2024-07-12

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

Hello everyone

Today, the fourth session of the 2024 Autonomous Driving OS Development Training Camp - Beginner Camp - Guided Learning was completed

Day 2: Practice Rust language with rustlings - Move Semantics

alt
alt
alt

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

How to solve the problem of no permission when submitting code

alt
alt

aciton

alt

Reference development environment configuration

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

My topic

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

Create an ssh key for cloning github code via ssh. In Linux environment, use the command ssh-keygen -t rsa -b 4096 -C "your email" to create an ssh key. Just press Enter for all the options below. Then use cat ~/.ssh/id_rsa.pub

Primitive Types

Rust has a couple of basic types that are directly implemented into the compiler. In this section, we'll go through the most important ones.

Further information

alt

The Slice Type Slices let you reference a contiguous sequence of elements in a collection rather than the whole collection.

A slice is a kind of reference, so it does not have ownership.

alt
alt

In Rust, a slice is a view of a contiguous range of elements from an array or another slice. To create a slice, you specify the start and end position of the slice (not including the end index). a If you want to get[2, 3, 4] For this slice, you need to start from index 1 and end at index 4 (excluding index 4).

Here's how to fix your test code:

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

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

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

In this example,&a[1..4] Created aa The slice starts at index 1 and ends at index 3, because Rust's slice syntax is a left-closed and right-open interval (including the start but not the end).nice_slice It contains the arraya middle[2, 3, 4]

Type rustlings watch in the command line to start the code editing loop experiment

alt

Understanding Ownership

Ownership is Rust’s most unique feature and has deep implications for the rest of the language.

It enables Rust to make memory safety guarantees without needing a garbage collecto

What Is Ownership?

alt

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 an owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped

alt
alt

The code example itself is correct and demonstrates the concepts of ownership and copying in Rust. However, if you want to make sure your code compiles and runs, and want to address some possible improvements or corrections, here are some suggestions:

  1. make sure takes_ownership andmakes_copy Function inmain Function defined before, because functions in Rust need to be declared before they can be used.

  2. add notes, explain why s existtakes_ownership After calling, it cannot be used.x existmakes_copy It can still be used after being called.

  3. Add to main Function return type, although this is not necessary in simple programs, it is good programming practice.

  4. Using Rust's formatter,for example rustfmt, to keep the code clean.

Here is the modified code according to the above suggestions:

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 this revised version, we added main Function return type(),this means main The function does not return any value. Also, a comment is added to explain whys existtakes_ownership After calling, it cannot be used.x existmakes_copy In addition, we also show that we try totakes_ownership Use after callings will result in a compilation error, andx existmakes_copy The example can still be used after calling.

Vectors

Vectors are one of the most-used Rust data structures.

In other programminglanguages, they'd simply be called Arrays, but since Rust operates on a bit of a lower level, an array in Rust is stored on the stack (meaning it can't grow or shrink, and the size needs to be known at compile time), and a Vector is stored in the heap (where these restrictions do not apply).

Vectors are a bit of a later chapter in the book, but we think that they're useful enough to talk about them a bit earlier. We shall be talking about the other useful data structure, hash maps, later.

Further information

alt vec_map The function accepts aVec<i32> type (i.e. a vector of 32-bit integers), and then returns a newVec<i32>, each element in this new vector is twice the corresponding element in the original vector.

Specifically,vec_map In the functionmap andcollect This is how the method works:

  1. **v.iter()**: This method creates an iterator that traverses v Each element of the vector.iter The iterator returned by the method provides an immutable reference to the elements in the vector.

  2. **.map(|element| { element * 2 })**:map The method accepts a closure (anonymous function), in this case, the closure accepts a single parameterelement(an immutable reference to an element in the vector), and then returns the result of multiplying the value of this element by 2. Operations within the closure element * 2 In fact, it is dereferencing the data pointed to by the reference (through* operator) and perform a multiplication operation.

  3. **.collect()**:map The method returns a lazy iterator that applies the operations in the closure one by one but does not collect the results immediately.collect method is called to pass thesemap The processed elements are collected into a newVec<i32> Vector.

use map andcollect The benefit of is that they allow you to perform operations on each element in a collection in a declarative way and collect the results into a new collection without having to write explicit loops. This makes the code more concise and more in line with the Rust style.

Here is a tutorial on how to use vec_map Example of a function:

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

In this example,vec_map take overoriginal_vec A new vector is created by usingdoubled_vec, which contains twice the value of each element of the original vector, and prints it out.

Move Semantics

These exercises are adapted from pnkfelix's Rust Tutorial -- Thank you Felix!!!

Further information

For this section, the book links are especially important.

move_semantics1.rs

alt

move_semantics2.rs

alt
alt

// move_semantics5.rs

alt

Mutable references in Rust have a rule: you can have any number of immutable references or at most one mutable reference at any given time. This rule ensures memory safety and prevents data races.

In the code provided, there is a problem:main Function attempts to create two mutable referencesy andz, both refer to the same variable x. This is not allowed because Rust's borrowing rules do not allow multiple mutable references to the same variable in the same scope. That's why the code does not compile.

To fix this, you need to ensure that only one mutable reference exists at any given time. Here is the fixed 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 this fixed version, we create an inner scope to ensure that only one mutable reference exists at any time. First, we create y , operates on it, and then the scope ends.y is no longer valid. After that, we createdz Scope, againx This obeys Rust's borrowing rules, allowing the code to compile and run as expected.

alt
alt
alt

This compilation error message points out a lifetime problem in Rust code. The problem is string_uppercase In the implementation of the function, it attempts to assign a temporary value to a reference, which is not allowed.

Here is the line of code where the error occurs:

data = &data.to_uppercase();
  • 1

In this line of code,data.to_uppercase() Created aString A new instance of this instance is a temporary value, and its life cycle depends ondata However, an attempt is made to assign a reference to this temporary value todata is illegal because the temporary value is destroyed as soon as the statement ends, anddata Need a lifetime longer than a temporary value.

To fix this problem, we need to modify string_uppercase function, so that it accepts a mutable reference and calls it directly on the data pointed to by the referencemake_uppercase() method instead of trying to reassign the value. Here is the fixed code:

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

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

In this fix release, we removed mut data: &String middlemut,because data is already a mutable reference. Then we directlydata Callmake_uppercase(), so there won't be any problem with temporary values.make_uppercase() The method will be modifieddata ReferencedString Instances without creating newString

also,main The call to the function also needs to be updated accordingly to pass a mutable reference:

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

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

Now,string_uppercase Function Acceptdata a mutable reference to , modifying it directly, andmain In the functiondata is mutable and can bestring_uppercase This way the code compiles and works as expected.

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

This article is provided by mdnice Multi-platform release