Sintaxis básica de Rust

Variables, tipos básicos, funciones, comentarios y flujo de control, estos son conceptos de programación presentes en casi todos los lenguajes de programación.

Estos conceptos básicos existirán en cada programa de Rust, aprenderlos desde el principio te permitirá aprender a usar Rust a la máxima velocidad.

Variables

Primero, debe explicarse que Rust es un lenguaje fuertemente tipado, pero tiene la capacidad de inferir automáticamente el tipo de las variables. Esto es fácil de confundir con los lenguajes débilmente tipados.

De forma predeterminada, las variables en Rust son inmutables, a menos que se declaren como variables mutables con la palabra clave mut.

let a = 123;       // 不可变变量
let mut b = 10;  // 可变变量

Para declarar una variable, se necesita usarletla palabra clave. Por ejemplo:

let a = 123;

Los desarrolladores que solo han aprendido JavaScript son muy sensibles a esta frase; los que solo han aprendido C no la entienden bien.

Después de esta declaración, las siguientes tres líneas de código están prohibidas:

a = "abc";
a = 4.56; 
a = 456;

El error de la primera línea es que cuando se declara a como 123, a se determina como un número entero, y no se le puede asignar un valor de tipo cadena.

El error de la segunda línea es que la conversión automática pierde precisión numérica. El lenguaje Rust no permite conversiones automáticas de tipos de datos que pierdan precisión.

El error de la tercera línea es que a no es una variable mutable.

Los dos primeros errores son fáciles de entender, pero ¿qué significa el tercero? ¿Acaso a no es una variable?

Esto se relaciona con el diseño del lenguaje Rust para la seguridad en alta concurrencia: minimizar en la medida de lo posible, a nivel del lenguaje, que los valores de las variables puedan cambiar. Por lo tanto, el valor de a es inmutable. Pero esto no significa que a no sea una "variable" (variable en inglés); los documentos oficiales llaman a variables como a "variable inmutable".

Si una parte de nuestro programa se ejecuta asumiendo que el valor nunca cambiará, mientras que otra parte de nuestro código cambia ese valor, entonces la primera parte del código podría no funcionar según el diseño previsto. Los errores causados por esta razón son difíciles de encontrar después. Esta es la razón por la que el lenguaje Rust diseña este mecanismo.

Por supuesto, hacer que una variable sea "mutable" solo requiere unamutpalabra clave.

let mut a = 123;
a = 456;

Este programa es correcto.

Diferencia entre constantes y variables inmutables

Ya que una variable inmutable es inmutable, ¿no es entonces una constante? ¿Por qué se llama variable?

Aún hay diferencias entre variables y constantes. En Rust, el siguiente programa es válido:

let a = 123;   // 可以编译,但可能有警告,因为该变量没有被使用
let a = 456;

Pero si a es una constante, no es válido:

const a: i32 = 123;
let a = 456;

El valor de una variable se puede "reasignar", pero no puede cambiarse arbitrariamente antes de la "reasignación". Esto asegura que en la región posterior a cada "vinculación", el compilador pueda razonar completamente la lógica del programa. Aunque Rust tiene la capacidad de inferir tipos automáticamente, en algunos casos declarar el tipo es más conveniente:

let a: u64 = 123;

Aquí se declara a como una variable entera sin signo de 64 bits. Si no se declara el tipo, a se inferirá automáticamente como una variable entera con signo de 32 bits, lo cual tiene un gran impacto en el rango de valores de a.

Tipos de datos

Rust es un lenguaje de tipado estático; el tipo puede especificarse explícitamente al declarar una variable, pero generalmente se puede confiar en la inferencia de tipos.

Tipos básicos:i32 (entero con signo de 32 bits), u32 (entero sin signo de 32 bits), f64 (número de punto flotante de 64 bits), bool (tipo booleano), char (carácter)

Ejemplo

let x: i32 = 42;
let y: f64 = 3.14;
let is_true: bool = true;
let letter: char = 'A';

Funciones

Las funciones en Rust se definen mediante lafnpalabra clave; el tipo de retorno de la función se especifica mediante el símbolo de flecha->.

Ejemplo

fn add(a: i32, b: i32) -> i32 {
    a + b
}

Si la función no tiene valor de retorno, el tipo es por defecto()(es decir, tupla vacía).

Flujo de control

Expresión if

Ejemplo

let number = 7;
if number < 5 {
    println!("menor que 5");
} else {
    println!("mayor o igual que 5");
}

Bucle loop:loop es un bucle infinito en Rust, y se puede salir del bucle usando break.

Ejemplo

let mut counter = 0;
loop {
    counter += 1;
    if counter == 10 {
        break;
    }
}

Bucle while

Ejemplo

let mut number = 3;
while number != 0 {
    println!("{}!", number);
    number -= 1;
}

Bucle for

Ejemplo

for number in 1..4 {
    println!("{}!", number);
}

Propiedad (Ownership)

La propiedad en Rust es un mecanismo único de gestión de memoria; los conceptos centrales incluyen propiedad (ownership), préstamo (borrowing) y referencia (reference).

Reglas de propiedad:

  • Cada valor en Rust tiene un propietario.
  • Cada valor solo puede tener un propietario en cualquier momento.
  • Cuando el propietario sale del ámbito, el valor se elimina.
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权被转移给了 s2
// println!("{}", s1); // 此处编译会报错,因为 s1 已不再拥有该值

Préstamos y referencias:El préstamo permite referenciar datos sin obtener la propiedad, mediante&el símbolo.

fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s);  // 借用
    println!("The length of '{}' is {}.", s, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

Estructuras (Structs)

Las estructuras se usan para crear tipos personalizados; los campos pueden contener múltiples tipos de datos.

Ejemplo

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

let user1 = User {
    username: String::from("someusername"),
    email: String::from("[email protected]"),
    sign_in_count: 1,
    active: true,
};

Enumeraciones (Enums)

Las enumeraciones permiten definir uno de los posibles tipos de datos.

Ejemplo

enum IpAddrKind {
    V4,
    V6,
}

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Coincidencia de patrones (match)

match es una poderosa herramienta de flujo de control en Rust, similar a la declaración switch.

Ejemplo

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

Manejo de errores

Rust tiene dos formas principales de manejo de errores:Result<T, E>yOption<T>。

Result:

Ejemplo

enum Result<T, E> {
    Ok(T),
    Err(E),
}

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

Option:

Ejemplo

fn get_element(index: usize, vec: &Vec<i32>) -> Option<i32> {
    if index < vec.len() {
        Some(vec[index])
    } else {
        None
    }
}

Ciclo de vida de la propiedad y el préstamo

Las anotaciones de ciclo de vida se representan con'a, etc., pero en casos comunes el compilador las deduce automáticamente.

Ejemplo

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Sombreado (Shadowing)

El concepto de sombreado no es lo mismo que el "override" o "overload" en lenguajes orientados a objetos. El sombreado es lo que se describió antes como "reasignación"; se usaron comillas solo para referirse al concepto antes de presentarlo formalmente.

El sombreado se refiere al mecanismo por el cual el nombre de una variable puede reutilizarse:

Ejemplo

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x);
}

El resultado de ejecutar este programa:

The value of x is: 12

El sombreado no es lo mismo que la asignación de una variable mutable. El sombreado significa usar el mismo nombre para representar nuevamente otra entidad de variable, cuyo tipo, atributo de mutabilidad y valor pueden cambiar. Pero la asignación de una variable mutable solo puede cambiar el valor.

let mut s = "123";
s = s.len();

Este programa dará error: no se puede asignar un valor entero a una variable de cadena.

Otras extensiones