기술나눔

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-이해-소유권.html

코드를 제출하면 권한이 없다는 메시지가 나타납니다. 어떻게 나가나요?

대체
대체

행동

대체

참조 개발 환경 구성

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

내 주제

https://github.com/cicvedu/rustlings-학기-4-감시점

SSH를 통해 github 코드를 복제하려면 SSH 키를 만듭니다. Linux 환경에서는 ssh-keygen -t rsa -b 4096 -C "your Mailbox" 명령을 사용하여 SSH 키를 생성합니다. 다음 옵션을 모두 보려면 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 슬라이스는 인덱스 1에서 시작하고 인덱스 3에서 끝나는데, Rust의 슬라이싱 구문은 왼쪽이 닫히고 오른쪽이 열리는 간격(시작은 포함하지만 끝은 포함하지 않음)이기 때문입니다.그래서nice_slice 배열이 포함되어 있습니다.a 가운데[2, 3, 4]

코드 편집의 루프 실험을 시작하려면 명령줄에 Rustlings watch를 입력하세요.

대체

소유권 이해

소유권은 Rust의 가장 독특한 특징이며, 나머지 언어에 깊은 영향을 미칩니다.

이를 통해 Rust는 가비지 콜렉터가 필요 없이 메모리 안전을 보장할 수 있습니다.

소유권이란 무엇인가?

대체

소유권 규칙 먼저 소유권 규칙을 살펴보겠습니다. 규칙을 설명하는 예를 살펴보면서 다음 규칙을 염두에 두십시오.

  • Rust의 각 값에는 소유자가 있습니다.
  • 한 번에 한 명의 소유자만 있을 수 있습니다.
  • 소유자가 범위를 벗어나면 값이 삭제됩니다.

대체
대체

코드 샘플 자체는 정확하며 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 데이터 구조 중 하나입니다.

다른 프로그래밍 언어에서는 이를 단순히 배열이라고 부르지만 Rust는 조금 더 낮은 수준에서 동작하기 때문에 Rust의 배열은 스택에 저장됩니다(즉, 커지거나 작아질 수 없으며 컴파일 시점에 크기를 알아야 함). 반면 Vector는 힙에 저장됩니다(이러한 제한은 적용되지 않음).

벡터는 책의 후반부에 나오는 챕터지만, 우리는 벡터가 충분히 유용하다고 생각해서 조금 더 일찍 이야기할 겁니다. 다른 유용한 데이터 구조인 해시 맵에 대해서는 나중에 이야기하겠습니다.

추가 정보

대체 vec_map 기능은 다음을 수락하는 것입니다.Vec<i32> 유형(즉, 32비트 정수를 포함하는 벡터)을 입력한 다음 새 값을 반환합니다.Vec<i32>, 이 새 벡터의 각 요소는 원래 벡터의 해당 요소 크기의 두 배입니다.

구체적으로,vec_map 기능상map 그리고collect 이 방법은 다음과 같이 작동합니다.

  1. **v.iter()**: 이 메서드는 순회하는 반복자를 생성합니다. v 벡터의 각 요소.iter 메서드에서 반환된 반복자는 벡터의 요소에 대한 변경 불가능한 참조를 제공합니다.

  2. **.map(|element| { element * 2 })**:map 이 메소드는 클로저(익명 함수)를 허용합니다. 이 경우 클로저는 하나의 매개변수를 허용합니다.element (벡터의 요소에 대한 불변 참조)는 이 요소의 값에 2를 곱한 값을 반환합니다.클로저 내부 작업element * 2 실제로 참조가 가리키는 데이터를 역참조하고 있습니다.* 연산자) 곱셈 연산을 수행합니다.

  3. **.collect()**:map 이 메서드는 클로저의 작업을 하나씩 적용하지만 결과를 즉시 수집하지 않는 지연 반복자를 반환합니다.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, 원본 벡터의 각 요소 값의 두 배를 포함하고 이를 인쇄합니다.

이동 의미론

이러한 연습은 다음에서 적용되었습니다. 피엔크펠릭스'에스 Rust 튜토리얼 -- 펠릭스 씨, 고맙습니다!!!

추가 정보

이 섹션에서는 책 링크가 특히 중요합니다.

무브_시맨틱스1.rs

대체

무브_시맨틱스2.rs

대체
대체

// move_semantics5.rs

대체

Rust에는 변경 가능한 참조에 대한 규칙이 있습니다. 언제든지 불변 참조를 얼마든지 가질 수 있거나 최대 하나의 변경 가능한 참조를 가질 수 있습니다. 이 규칙은 메모리 안전을 보장하고 데이터 경합을 방지합니다.

제공된 코드에 문제가 있습니다.main 함수가 두 개의 변경 가능한 참조를 생성하려고 시도했습니다.y 그리고z, 둘 다 동일한 변수를 가리킵니다. x . 이는 Rust의 차용 규칙이 동일한 범위의 동일한 변수에 대한 여러 변경 가능한 참조를 허용하지 않기 때문에 허용되지 않습니다. 이것이 코드가 컴파일되지 않는 이유입니다.

이 문제를 해결하려면 주어진 시간에 변경 가능한 참조가 하나만 존재하는지 확인해야 합니다. 고정된 코드는 다음과 같습니다.

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

이 수정 버전에서는 내부 범위를 생성하여 언제든지 하나의 변경 가능한 참조만 존재하도록 보장합니다.먼저, 우리는 만들었습니다.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

이 글은 작성자입니다 엠디나이스다중 플랫폼 출판