Introducción a Rust

Introducción a Rust

Daniel García Moreno (@danigm)

<danigm@gnome.org>

https://danigm.net

Sobre mí

Qué es Rust

Rust

Rust es un lenguaje de programación de sistemas extremadamente rápido, previene fallas de segmentación y garantiza la seguridad de los hilos de ejecución.


// hola.rs
fn main() {
    println!("Hola Mundo!");
}

rustc hola.rs
./hola
Hola Mundo!

C/C++ No son lenguajes seguros

Lenguajes Seguros

Recolector de basura, no son eficientes

Por qué Rust

rustup y Cargo

Instalar y tener actualizado rust es realmente sencillo con rustup


curl https://sh.rustup.rs -sSf | sh
export PATH=$PATH:$HOME/.cargo/bin
Además se pueden instalar otros targets para compilación cruzada

rustup target list
rustup target install x86_64-pc-windows-gnu

Cargo

Cargo es una herramienta que nos permite gestionar proyectos rust, dependencias, compilar, generar documentación, hacer tests, etc...
cargo new --bin hola

# cat Cargo.toml
[package]
name = "hola"
version = "0.1.0"
authors = ["Daniel García Moreno "]

[dependencies]  

// cat src/main.rs
fn main() {
    println!("Hello, world!");
}  

cargo run
Hello, world!
cargo build --release
cargo check
cargo test
cargo doc
cargo search time
cargo add package
cargo install package
cargo publish

Sintaxis


extern crate chrono;
use chrono::prelude::*;

fn get_time() -> DateTime {
    let d = Local::now();
    d
}

/// función que suma los números de 0 a n
fn get_sum(n: i32) -> i32 {
    let mut x: i32 = 0;
    for i in 0..n {
        x += i;
    }

    x
}

fn dup(txt1: &str, txt2: &str) {
    println!("{0} {1} {0} {1}", txt1, txt2);
}

// función principal
fn main() {
    let t = get_time();
    println!("Hola Mundo! {}", t);

    let i = get_sum(10);
    println!("Resultado: {}", i);

    dup("esto", "otro");
}

Structs y Enums


pub struct Persona {
    nombre: String,
    apellido1: String,
    apellido2: Option,
    edad: i32,
    prof: Profesion,
}

impl Persona {
    pub fn new(nombre: String, apellidos: (String, String)) -> Persona {
        Persona {
            nombre: nombre,
            apellido1: apellidos.0,
            apellido2: Some(apellidos.1),
            edad: 0,
            prof: Profesion::Docotor(Profesion::Medicina),
        }
    }
}

enum Profesion {
    Informatica,
    Medicina,
    Doctor(Profesion),
    Estudiante(Profesion),
    Otro(String),
}

Gestión de memoria

Ownership


let v = vec![1, 2, 3];
let v2 = v;

let a = v[0]; // Error, intentamos usar v después de moverlo

Borrowing


let mut v = vec![1, 2, 3];
let v2 = &v; // referencia
let v3 = &v; // referencia

let a = v[0];

// Error, como hay referencias de lectura, no puede existir una referencia mutable
let v4 = &mut v;

let mut n = 3;
{
    let nref = &n;
    *nref = 5;
}

println!("n: {}", n);

Lifetimes


fn eliminar_prefijo<'a, 'b>(orig: &'a str,
                            prefijo: &'b str)
                            -> &'a str {
    &orig[prefijo.len()..]
}

fn main() {
    let x;
    {
        let a = "texto:hola".to_string();
        let b = "texto:";

        x = eliminar_prefijo(&a, &b);
    }

    println!("X: {}", x);
}

Gestión de errores

Result<T, E>


fn division(n: i32, d: i32) -> Result<i32, &str> {
    if d == 0 {
        return "No se puede dividir por cero";
    }
    n / d
}
fn propagando() -> Result<i32, &str> {
    let n = division(3, 0)?;
    println!("resultado de la división: {}", n);
    n
}
fn main() {
    let r = propagando();
    if r.is_ok() {
        // accediendo al valor, si no es ok, fallará
        println!("valor: {}", r.unwrap());
    }
    assert_eq!(r, Err("No se puede dividir por cero"));
}

Option

Option<T>


let mut n: Option<i32> = None;

// Error porque es un option, no un i32
let suma = n + 3;
if n.is_some() {
    let suma = n.unwrap() + 3;
}

n = Some(5);
let suma = match n {
    Some(i) => i + 3,
    None => 0,
};

Pattern matching

Pattern matching


match n {
    i if i < 0 => {
        println!("número negativo: {}", i);
    },
    5 => println!("n es 5"),
    _ => println!("cualquier cosa"),
};

if let Some(n) = funcion_devuelve_option(23) {
    println!("{}", n + 4);
}

if let Err(error) = funcion_result(3, 5) {
    println!("ha ocurrido un error");
    return;
}

let n1 = match n {
    i if i > 0 => -i,
    0 => 0,
    _ => 42,
};

Traits

Traits


trait Operaciones : PartialEq {
    fn area(&self) -> f64;
    fn perimetro(&self) -> f64;
    fn print(&self) {
        println!("Area: {}", self.area());
        println!("Perímetro: {}", self.perimetro());
    }
}

use std::f64::consts::PI;
#[derive(Debug, PartialEq)]
struct Circulo {
    centro: (i32, i32),
    radio: i32,
}
impl Operaciones for Circulo {
    fn area(&self) -> f64 { PI * self.radio.pow(2) as f64 }
    fn perimetro(&self) -> f64 { use std::f64::consts::PI; PI * self.radio as f64 * 2.0 }
}
let c = Circulo { centro: (0, 0), radio: 5 };
c.print();

#[derive(Debug)]
struct Rectangulo {
    alto: i32,
    largo: i32
}

impl PartialEq for Rectangulo {
    fn eq(&self, other: &Rectangulo) -> bool {
        self.alto * self.largo == other.alto * other.largo
    }
}

impl Operaciones for Rectangulo {
    fn area(&self) -> f64 {
        (self.alto * self.largo) as f64
    }
    fn perimetro(&self) -> f64 {
        (self.alto * 2 + self.largo * 2) as f64
    }
    fn print(&self) {
        println!(" * Area     : {} m2", self.area());
        println!(" * Perímetro: {} m", self.perimetro());
    }
}

let r = Rectangulo {
    alto: 10,
    largo: 5,
};
r.print();

Macros


macro_rules! cat {
    ( $( $x:expr ),* ) => {{
        let mut base = String::new();
        $(
            base = base + &format!("-{}", $x);
        )*
        base[..]
    }}
}

println!("CAT: {}", cat!("hola", "adios", "123"));

macro_rules! foo {
    (x => $e:expr) => (println!("mode X: {}", $e));
    (y => $e:expr) => (println!("mode Y: {}", $e));
}

foo!(y => 3);

Crates y módulos

Documentación y tests

Doc


/// Constructs a new `Rc<T>`.
///
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
pub fn new(value: T) -> Rc<T> {
    // Implementation goes here.
}

Tests


pub fn add_two(a: i32) -> i32 { a + 2 }

#[cfg(test)]
mod tests {
    use super::add_two;

    #[test]
    fn it_works() {
        assert_eq!(4, add_two(2));
    }
}

/// This function adds two to its argument.
///
/// # Examples
///
/// ```
/// use adder::add_two;
///
/// assert_eq!(4, add_two(2));
/// ```
pub fn add_two(a: i32) -> i32 { a + 2 }
https://danigm.net
danigm@gnome.org
http://rust-lang.org