Compartir tecnología

Día 2 Practica el lenguaje Rust con susurros-Move Semantics

2024-07-12

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

Hola a todos

Hoy completé el campamento de capacitación sobre desarrollo del sistema operativo de conducción autónoma 2024: la cuarta fase del campamento de primaria - Tutorial

Día 2 Practica el lenguaje Rust con susurros-Move Semantics

alt
alt
alt

https://doc.rust-lang.org/stable/book/ch04-00-entendiendo-la-propiedad.html

Cuando envío el código, recibo un mensaje que dice que no tengo permiso. ¿Cómo salgo?

alt
alt

acción

alt

Configuración del entorno de desarrollo de referencia.

https://rcore-os.cn/libro-tutorial-de-arceos/caps01-02.html

mi tema

https://github.com/cicvedu/rustlings-semestre-4-puntos-de-vigilancia

Cree una clave ssh para clonar el código de github a través de ssh. En el entorno Linux, utilice el comando ssh-keygen -t rsa -b 4096 -C "su buzón" para crear una clave ssh. Simplemente presione Entrar para ver todas las opciones siguientes. Luego usa cat ~/.ssh/id_rsa.pub

Tipos primitivos

Rust tiene un par de tipos básicos que se implementan directamente en el compilador. En esta sección, repasaremos los más importantes.

Más información

alt

Tipo Slice Las rebanadas le permiten hacer referencia a una secuencia contigua de elementos en una colección en lugar de a toda la colección.

Una porción es una especie de referencia, por lo que no tiene propiedad.

alt
alt

En Rust, un sector es una vista que hace referencia a una secuencia de elementos contiguos de una matriz u otro sector. Para crear un sector, debe especificar las posiciones inicial y final del sector (sin incluir el índice de la posición final).en matriza en si quieres conseguir[2, 3, 4] Para este segmento, debe comenzar en el índice 1 y terminar en el índice 4 (sin incluir el índice 4).

A continuación le indicamos cómo corregir su código de prueba:

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

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

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

En este ejemplo,&a[1..4] Creó una matriz dea El segmento comienza en el índice 1 y termina en el índice 3, porque la sintaxis de corte de Rust es un intervalo cerrado por la izquierda y abierto por la derecha (incluido el inicio, pero no el final).entoncesnice_slice Contiene la matriza medio[2, 3, 4]

Ingrese Rustlings Watch en la línea de comando para iniciar el experimento de bucle de edición del código.

alt

Entendiendo la propiedad

La propiedad es la característica más exclusiva de Rust y tiene profundas implicaciones para el resto del lenguaje.

Permite a Rust realizar garantías de seguridad de memoria sin necesidad de un recolector de basura.

¿Qué es la propiedad?

alt

Reglas de propiedad Primero, echemos un vistazo a las reglas de propiedad. Tenga en cuenta estas reglas mientras trabajamos con los ejemplos que las ilustran:

  • Cada valor en Rust tiene un propietario.
  • Sólo puede haber un propietario a la vez.
  • Cuando el propietario quede fuera del alcance, el valor se eliminará.

alt
alt

El ejemplo de código en sí es correcto y demuestra los conceptos de propiedad y copia en Rust. Sin embargo, si desea asegurarse de que su código se compila y se ejecuta, y desea abordar algunas posibles mejoras o correcciones, aquí tiene algunas sugerencias:

  1. Cerciorarse takes_ownership ymakes_copy funcionar enmain función definida antes, porque las funciones en Rust deben declararse primero y luego usarse.

  2. agregar notas, explicar por qué s existirtakes_ownership no se puede utilizar después de haber sido llamado, yx existirmakes_copy Todavía se puede utilizar después de haber sido llamado.

  3. añadir main tipo de retorno de función, aunque esto no es necesario en programas simples, es una buena práctica de programación.

  4. Usando las herramientas de formato de Rust,Por ejemplo rustfmt, para mantener el código limpio.

Aquí está el código modificado basado en las sugerencias anteriores:

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

En esta versión revisada hemos agregado main tipo de retorno de función(),esto significa main La función no devuelve ningún valor.Además, se añaden comentarios para explicar por qué.s existirtakes_ownership no se puede utilizar después de haber sido llamado, yx existirmakes_copy Sigue siendo válido después de llamar.Además, mostramos intentos detakes_ownership Usar después de llamars Ejemplos que causarían errores de compilación, yx existirmakes_copy Ejemplos que aún se pueden utilizar después de llamar.

Vectores

Los vectores son una de las estructuras de datos de Rust más utilizadas.

En otros lenguajes de programación, simplemente se llamarían matrices, pero como Rust opera en un nivel un poco más bajo, una matriz en Rust se almacena en la pila (lo que significa que no puede crecer ni reducirse, y el tamaño debe conocerse en el momento de la compilación), y un vector se almacena en el montón (donde estas restricciones no se aplican).

Los vectores son un capítulo más adelante en el libro, pero creemos que son lo suficientemente útiles como para hablar de ellos un poco antes. Hablaremos de otra estructura de datos útil, los mapas hash, más adelante.

Más información

alt vec_map La función es aceptar unaVec<i32> tipo (es decir, un vector que contiene un entero de 32 bits), luego devuelve un nuevoVec<i32>, cada elemento de este nuevo vector tiene el doble de tamaño que el elemento correspondiente del vector original.

Específicamente,vec_map en funciónmap ycollect El método funciona así:

  1. **v.iter()**: Este método crea un iterador que atraviesa v cada elemento del vector.iter El iterador devuelto por el método proporciona una referencia inmutable a los elementos del vector.

  2. **.map(|element| { element * 2 })**:map El método acepta un cierre (función anónima), en este caso el cierre acepta un parámetroelement (una referencia inmutable a un elemento en el vector), luego devuelve el valor de este elemento multiplicado por 2.Operaciones dentro de cierreselement * 2 De hecho, está desreferenciando los datos señalados por la referencia (a través de* operador) y realizar operaciones de multiplicación.

  3. **.collect()**:map El método devuelve un iterador diferido que aplica las operaciones del cierre una por una, pero no recopila los resultados inmediatamente.collect Se llama al método para pasar estosmap Los elementos procesados ​​se recogen en un nuevoVec<i32> en vectores.

usar map ycollect El beneficio es que le permiten realizar operaciones en cada elemento de la colección de forma declarativa y recopilar los resultados en una nueva colección sin la necesidad de escribir un bucle explícito. De esta forma el código es más conciso y más consistente con el estilo Rust.

Aquí se explica cómo utilizar vec_map Ejemplo de función:

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

En este ejemplo,vec_map tomar el controloriginal_vec referencia, creando un nuevo vectordoubled_vec, que contiene el doble del valor de cada elemento del vector original y lo imprime.

Semántica de movimiento

Estos ejercicios están adaptados de Pnk Félix's Tutorial de óxido - ¡¡¡Gracias Félix!!!

Más información

Para esta sección, los enlaces a libros son especialmente importantes.

movimiento_semantica1.rs

alt

movimiento_semantica2.rs

alt
alt

// semantica_de_movimiento5.rs

alt

Existe una regla para las referencias mutables en Rust: en cualquier momento dado, puede tener cualquier cantidad de referencias inmutables, o como máximo una referencia mutable. Esta regla garantiza la seguridad de la memoria y evita las carreras de datos.

En el código proporcionado, hay un problema:main La función intentó crear dos referencias mutables.y yz, ambos apuntan a la misma variable x . Esto no está permitido porque las reglas de préstamo de Rust no permiten múltiples referencias mutables a la misma variable en el mismo alcance. Por eso el código no se compila.

Para solucionar este problema, debe asegurarse de que solo exista una referencia mutable en un momento dado. Aquí está el código fijo:

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

En esta versión fija, nos aseguramos de que solo exista una referencia mutable en cualquier momento mediante la creación de un alcance interno.Primero, creamosy alcance, se realizan operaciones en él, y luego este alcance finaliza,y Ya no es valido.Después de eso, creamosz alcance, nuevamente parax Realizar operaciones. Esto sigue las reglas de préstamo de Rust para que el código se compile y se ejecute como se esperaba.

alt
alt
alt

Este mensaje de error de compilación apunta a un problema del ciclo de vida en el código Rust.El problema esstring_uppercase En la implementación de la función se intenta asignar un valor temporal a una referencia, lo cual no está permitido.

Aquí está la línea de código que sale mal:

data = &data.to_uppercase();
  • 1

En esta línea de código,data.to_uppercase() Creo unString una nueva instancia dedata prestado.Sin embargo, intente asignar una referencia a este valor temporal paradata es ilegal porque el valor temporal se destruye inmediatamente al final de la declaración, ydata Requiere una vida útil más larga que los valores temporales.

Para solucionar este problema necesitamos modificar string_uppercase Una función que acepta una referencia mutable y se llama directamente sobre los datos señalados por la referencia.make_uppercase() método en lugar de intentar reasignar. Aquí está el código fijo:

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

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

En esta versión fija hemos eliminado mut data: &String mediomut,porque data Ya es una referencia mutable.Entonces nosotros directamentedata recurrirmake_uppercase(), por lo que no habrá problema con los valores temporales.make_uppercase() El método será modificado.data puntos de referencia aString instancia sin crear una nuevaString

también,main La llamada dentro de la función también debe actualizarse en consecuencia para pasar una referencia mutable:

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

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

Ahora,string_uppercase la función aceptadata una referencia mutable, modifíquela directamente ymain en funcióndata es mutable y puede serstring_uppercase modificado. De esta manera el código se compila y funciona como se esperaba.

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 artículo está escrito por mdnicePublicación multiplataforma