Compartilhamento de tecnologia

Dia 2 Pratique a linguagem Rust com farfalhar-Move Semântica

2024-07-12

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

Olá pessoal

Hoje concluí o acampamento de treinamento de desenvolvimento de sistema operacional de direção autônoma 2024 - a quarta fase do acampamento elementar - tutorial

Dia 2 Pratique a linguagem Rust com farfalhar-Move Semântica

alt
alt
alt

https://doc.rust-lang.org/stable/book/ch04-00-entendendo-a-propriedade.html

Quando envio o código, recebo uma mensagem informando que não tenho permissão. Como faço para sair?

alt
alt

ação

alt

Configuração do ambiente de desenvolvimento de referência

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

meu tópico

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

Crie uma chave ssh para clonar o código do github via ssh. No ambiente Linux, use o comando ssh-keygen -t rsa -b 4096 -C "your mailbox" para criar uma chave ssh. Basta pressionar Enter para todas as opções a seguir. Então use cat ~/.ssh/id_rsa.pub

Tipos primitivos

Rust tem alguns tipos básicos que são implementados diretamente no compilador. Nesta seção, veremos os mais importantes.

Outras informações

alt

O tipo de fatia As fatias permitem que você faça referência a uma sequência contígua de elementos em uma coleção em vez de toda a coleção.

Uma fatia é um tipo de referência, portanto não tem propriedade.

alt
alt

No Rust, uma fatia é uma visualização que faz referência a uma sequência de elementos contíguos de um array ou outra fatia. Para criar uma fatia, você precisa especificar as posições inicial e final da fatia (sem incluir o índice da posição final).em matriza se você quiser obter[2, 3, 4] Para esta fatia, você precisa começar no índice 1 e terminar no índice 4 (excluindo o índice 4).

Veja como corrigir seu código de teste:

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

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

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

Neste exemplo,&a[1..4] Criou uma matriz dea A fatia começa no índice 1 e termina no índice 3, porque a sintaxe de fatiamento do Rust é um intervalo fechado à esquerda e aberto à direita (incluindo o início, mas não o fim).entãonice_slice Ele contém a matriza meio[2, 3, 4]

Digite rustlings watch na linha de comando para iniciar o experimento de loop de edição do código

alt

Compreendendo a propriedade

Propriedade é o recurso mais exclusivo do Rust e tem implicações profundas para o restante da linguagem.

Ele permite que o Rust faça garantias de segurança de memória sem precisar de um coletor de lixo

O que é propriedade?

alt

Regras de Propriedade Primeiro, vamos dar uma olhada nas regras de propriedade. Tenha essas regras em mente enquanto trabalhamos nos exemplos que as ilustram:

  • Cada valor em Rust tem um proprietário.
  • Só pode haver um proprietário por vez.
  • Quando o proprietário sai do escopo, o valor será descartado

alt
alt

O exemplo de código em si está correto e demonstra os conceitos de propriedade e cópia em Rust. No entanto, se você quiser ter certeza de que seu código será compilado e executado e quiser abordar algumas possíveis melhorias ou correções, aqui estão algumas sugestões:

  1. certificar-se takes_ownership emakes_copy funcionar emmain função definida antes, porque as funções em Rust precisam ser declaradas primeiro e depois usadas.

  2. adicionar notas, explique por que s existirtakes_ownership não pode ser usado depois de ser chamado, ex existirmakes_copy Ele ainda pode ser usado após ser chamado.

  3. adicionar à main tipo de retorno de função, embora isso não seja necessário em programas simples, é uma boa prática de programação.

  4. Usando as ferramentas de formatação do Rust,por exemplo rustfmt, para manter o código limpo.

Aqui está o código modificado com base nas sugestões acima:

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

Nesta versão revisada, adicionamos main tipo de retorno de função(),isso significa main A função não retorna nenhum valor.Além disso, comentários são adicionados para explicar por ques existirtakes_ownership não pode ser usado depois de ser chamado, ex existirmakes_copy Ainda válido após ligar.Além disso, mostramos tentativas detakes_ownership Use depois de ligars Exemplos que causariam erros de compilação ex existirmakes_copy Exemplos que ainda podem ser usados ​​após a chamada.

Vetores

Vetores são uma das estruturas de dados Rust mais utilizadas.

Em outras linguagens de programação, eles seriam simplesmente chamados de Arrays, mas como Rust opera em um nível um pouco mais baixo, um array em Rust é armazenado na pilha (o que significa que ele não pode aumentar ou diminuir, e o tamanho precisa ser conhecido em tempo de compilação), e um Vector é armazenado no heap (onde essas restrições não se aplicam).

Os vetores são um capítulo um pouco mais adiante no livro, mas achamos que são úteis o suficiente para falar sobre eles um pouco antes. Falaremos sobre a outra estrutura de dados útil, mapas de hash, mais tarde.

Outras informações

alt vec_map A função é aceitar umVec<i32> tipo (ou seja, um vetor contendo um número inteiro de 32 bits), então retorna um novoVec<i32>, cada elemento neste novo vetor tem o dobro do tamanho do elemento correspondente no vetor original.

Especificamente,vec_map em funçãomap ecollect O método funciona assim:

  1. **v.iter()**: Este método cria um iterador que percorre v cada elemento do vetor.iter O iterador retornado pelo método fornece uma referência imutável aos elementos do vetor.

  2. **.map(|element| { element * 2 })**:map O método aceita um encerramento (função anônima), neste caso o encerramento aceita um parâmetroelement (uma referência imutável a um elemento no vetor) e então retorna o valor deste elemento multiplicado por 2.Operações dentro de fechamentoselement * 2 Na verdade, você está desreferenciando os dados apontados pela referência (via* operador) e realizar operações de multiplicação.

  3. **.collect()**:map O método retorna um iterador lento que aplica as operações no fechamento uma por uma, mas não coleta os resultados imediatamente.collect método é chamado para passar estesmap Os elementos processados ​​são coletados em um novoVec<i32> em vetor.

usar map ecollect A vantagem é que eles permitem realizar operações em cada elemento da coleção de forma declarativa e coletar os resultados em uma nova coleção sem a necessidade de escrever loops explícitos. Desta forma o código fica mais conciso e mais consistente com o estilo Rust.

Aqui está um como usar vec_map Exemplo de função:

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

Neste exemplo,vec_map assumiroriginal_vec referência, criando um novo vetordoubled_vec, que contém o dobro do valor de cada elemento do vetor original e o imprime.

Mover Semântica

Esses exercícios são adaptados de pnkfelix's Tutorial de Ferrugem -- Obrigado Félix!!!

Outras informações

Para esta seção, os links dos livros são especialmente importantes.

move_semantics1.rs

alt

move_semantics2.rs

alt
alt

// move_semantics5.rs

alt

Existe uma regra para referências mutáveis ​​no Rust: a qualquer momento, você pode ter qualquer número de referências imutáveis, ou no máximo uma referência mutável. Esta regra garante a segurança da memória e evita corridas de dados.

No código fornecido, há um problema:main A função tentou criar duas referências mutáveisy ez, ambos apontam para a mesma variável x . Isso não é permitido porque as regras de empréstimo do Rust não permitem múltiplas referências mutáveis ​​para a mesma variável no mesmo escopo. É por isso que o código não compila.

Para corrigir esse problema, você precisa garantir que exista apenas uma referência mutável em um determinado momento. Aqui está o código fixo:

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

Nesta versão fixa, garantimos que exista apenas uma referência mutável por vez, criando um escopo interno.Primeiro, criamosy escopo, uma operação é executada nele e então esse escopo termina,y Não muito valido.Depois disso, criamosz escopo, novamente parax Realizar operações. Isso segue as regras de empréstimo do Rust para que o código seja compilado e executado conforme o esperado.

alt
alt
alt

Esta mensagem de erro de compilação aponta para um problema de ciclo de vida no código Rust.O problema éstring_uppercase Na implementação da função, tenta atribuir um valor temporário a uma referência, o que não é permitido.

Aqui está a linha de código que dá errado:

data = &data.to_uppercase();
  • 1

Nesta linha de código,data.to_uppercase() Criou umString Uma nova instância dedata emprestado.No entanto, tente atribuir uma referência a este valor temporário paradata é ilegal porque o valor temporário é destruído imediatamente no final da declaração, edata Requer uma vida útil mais longa do que valores temporários.

Para corrigir esse problema, precisamos modificar string_uppercase Uma função que aceita uma referência mutável e a chama diretamente nos dados apontados por essa referênciamake_uppercase() método em vez de tentar reatribuir. Aqui está o código fixo:

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

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

Nesta versão fixa removemos mut data: &String meiomut,porque data Já é uma referência mutável.Então nós diretamentedata ligarmake_uppercase(), portanto não haverá problemas com valores temporários.make_uppercase() O método será modificadodata pontos de referência paraString instância sem criar uma novaString

também,main A chamada dentro da função também precisa ser atualizada de acordo para passar uma referência mutável:

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

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

Agora,string_uppercase função aceitadata uma referência mutável, modifique-a diretamente emain em funçãodata é mutável e pode serstring_uppercase modificado. Desta forma o código compila e funciona conforme o esperado.

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

Este artigo foi escrito por mdlegalPublicação multiplataforma