lunes, 4 de septiembre de 2023

Vec en Rust


Vec es el búfer estándar de tamaño variable de Rust, veamos un ejemplo:


fn main() {

    let mut v1 = Vec::new();

    v1.push(42);

    println!("v1: len = {}, capacity = {}", v1.len(), v1.capacity());


    let mut v2 = Vec::with_capacity(v1.len() + 1);

    v2.extend(v1.iter());

    v2.push(9999);

    println!("v2: len = {}, capacity = {}", v2.len(), v2.capacity());


    // Canonical macro to initialize a vector with elements.

    let mut v3 = vec![0, 0, 1, 2, 3, 4];


    // Retain only the even elements.

    v3.retain(|x| x % 2 == 0);

    println!("{v3:?}");


    // Remove consecutive duplicates.

    v3.dedup();

    println!("{v3:?}");

}


Vec implementa Deref<Target = [T]>, lo que significa que puedes llamar a métodos de slice.

  • Vec es un tipo de colección, junto con String y HashMap. Los datos que contiene se almacenan en el monticulo. Esto significa que no es necesario conocer la cantidad de datos en el momento de la compilación. Puede crecer o reducirse en tiempo de ejecución.
  • Observe que Vec<T> también es un tipo genérico, pero no es necesario especificar T explícitamente. Como siempre con la inferencia de tipo Rust, la T se estableció durante la primera llamada push.
  • vec![...] es una macro canónica para usar en lugar de Vec::new() y admite agregar elementos iniciales al vector.
  • Para indexar el vector se puede utilizar [], pero entrarán en pánico si están fuera de los límites. Alternativamente, usar get devolverá un Option. La función pop eliminará el último elemento.

domingo, 3 de septiembre de 2023

String en Rust


 String es el búfer de cadena UTF-8 estándar:


fn main() {

    let mut s1 = String::new();

    s1.push_str("Hello");

    println!("s1: len = {}, capacity = {}", s1.len(), s1.capacity());


    let mut s2 = String::with_capacity(s1.len() + 1);

    s2.push_str(&s1);

    s2.push('!');

    println!("s2: len = {}, capacity = {}", s2.len(), s2.capacity());


    let s3 = String::from("🇨🇭");

    println!("s3: len = {}, number of chars = {}", s3.len(),

             s3.chars().count());

}


Si ejecutamos este código: 


Compiling hello_cargo v0.1.0 

    Finished dev [unoptimized + debuginfo] target(s) in 0.65s

     Running `target/debug/hello_cargo`

s1: len = 5, capacity = 8

s2: len = 6, capacity = 6

s3: len = 8, number of chars = 2

String implementa Deref<Target = str>, lo que significa que puedes llamar a todos los métodos str en un String.

  • String::new devuelve una nueva cadena vacía, se usa String::with_capacity cuando se sabe cuántos caracteres tiene la cadena.
  • String::len devuelve el tamaño de la cadena en bytes (que puede ser diferente de su longitud en caracteres).
  • String::chars devuelve un iterador sobre los caracteres reales. Un carácter puede ser diferente de lo que un humano considerará un "carácter" debido a los grupos de grafemas.
  • Cuando las personas se refieren a cadenas, podrían estar hablando de &str o String.
  • Cuando un tipo implementa Deref<Target = T>, el compilador le permitirá llamar de forma transparente a métodos desde T.
  • String implementa Deref<Target = str> que le da acceso de forma transparente a los métodos de str.
  • String se implementa como un contenedor alrededor de un vector de bytes; muchas de las operaciones que ve admitidas en vectores también se admiten en String, pero con algunas garantías adicionales.


sábado, 2 de septiembre de 2023

Rust Standard Library


Rust tiene una biblioteca estándar que ayuda a establecer un conjunto de tipos comunes utilizados por las bibliotecas y los programas de Rust. De esta manera, dos bibliotecas pueden funcionar juntas sin problemas porque ambas usan el mismo tipo de cadena, por ejemplo.

Los tipos de vocabulario comunes incluyen:

  • Option y Result: se utilizan para valores opcionales y manejo de errores.
  • String: el tipo de cadena predeterminado utilizado para los datos propios.
  • Vec: un vector extensible estándar.
  • HashMap: un tipo de mapa hash con un algoritmo hash configurable.
  • Box: un puntero propio para datos asignados al monticulo.
  • Rc: un puntero compartido.

De hecho, Rust contiene varias capas de la biblioteca estándar: core, alloc y std.

core incluye los tipos y funciones más básicos que no dependen de libc, asignador o incluso de la presencia de un sistema operativo.

alloc incluye tipos que requieren un asignador de monticulo global, como Vec, Box y Arc.

Option y Result en Rust


Los tipos representan datos opcionales:


fn main() {

    let numbers = vec![10, 20, 30];

    let first: Option<&i8> = numbers.first();

    println!("first: {first:?}");


    let idx: Result<usize, usize> = numbers.binary_search(&10);

    println!("idx: {idx:?}");

}

Si ejecuto este codigo : 

cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

    Finished dev [unoptimized + debuginfo] target(s) in 0.75s

     Running `target/debug/hello_cargo`

first: Some(10)

idx: Ok(0)


Si por ejemplo busco el 39 : 

    let idx: Result<usize, usize> = numbers.binary_search(&39); 

    println!("idx: {idx:?}");


Si corremos esto : 

cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

    Finished dev [unoptimized + debuginfo] target(s) in 0.27s

     Running `target/debug/hello_cargo`

first: Some(10)

idx: Err(3)

El resultado es Err y no Ok. 

  • Option y Result se utilizan ampliamente no solo en la biblioteca estándar.
  • La Option <&T> tiene cero espacio adicional en comparación con &T.
  • El resultado es el tipo estándar para implementar el manejo de errores.
  • binario_search devuelve Result<usize, usize>.
    • Si se encuentra, Result::Ok contiene el índice donde se encuentra el elemento.
    • De lo contrario, Result::Err contiene el índice donde se debe insertar dicho elemento.

viernes, 1 de septiembre de 2023

Metodos en Rust parte 3


Veamos un ejemplo de metodos:

#[derive(Debug)]

struct Race {

    name: String,

    laps: Vec<i32>,

}


impl Race {

    fn new(name: &str) -> Race {  // No receiver, a static method

        Race { name: String::from(name), laps: Vec::new() }

    }


    fn add_lap(&mut self, lap: i32) {  // Exclusive borrowed read-write access to self

        self.laps.push(lap);

    }


    fn print_laps(&self) {  // Shared and read-only borrowed access to self

        println!("Recorded {} laps for {}:", self.laps.len(), self.name);

        for (idx, lap) in self.laps.iter().enumerate() {

            println!("Lap {idx}: {lap} sec");

        }

    }


    fn finish(self) {  // Exclusive ownership of self

        let total = self.laps.iter().sum::<i32>();

        println!("Race {} is finished, total lap time: {}", self.name, total);

    }

}


fn main() {

    let mut race = Race::new("Monaco Grand Prix");

    race.add_lap(70);

    race.add_lap(68);

    race.print_laps();

    race.add_lap(71);

    race.print_laps();

    race.finish();

    // race.add_lap(42);

}

Lo ejecutamos : 

cargo run

   Compiling hello_cargo v0.1.0 

     Finished dev [unoptimized + debuginfo] target(s) in 0.62s

     Running `target/debug/hello_cargo`

Recorded 2 laps for Monaco Grand Prix:

Lap 0: 70 sec

Lap 1: 68 sec

Recorded 3 laps for Monaco Grand Prix:

Lap 0: 70 sec

Lap 1: 68 sec

Lap 2: 71 sec

Race Monaco Grand Prix is finished, total lap time: 209


Los cuatro métodos aquí utilizan un receptor de método diferente.

Si intentamos llamar a finalizar dos veces o descomentamos // race.add_lap(42); , lanza este error : 

cargo run

   Compiling hello_cargo v0.1.0 

error[E0382]: borrow of moved value: `race`

  --> src/main.rs:37:5

   |

30 |     let mut race = Race::new("Monaco Grand Prix");

   |         -------- move occurs because `race` has type `Race`, which does not implement the `Copy` trait

...

36 |     race.finish();

   |          -------- `race` moved due to this method call

37 |     race.add_lap(42);

   |     ^^^^^^^^^^^^^^^^ value borrowed here after move

   |

note: this function takes ownership of the receiver `self`, which moves `race`

  --> src/main.rs:23:15

   |

23 |     fn finish(self) {  // Exclusive ownership of self

   |               ^^^^

For more information about this error, try `rustc --explain E0382`.

error: could not compile `hello_cargo` due to previous error

Y es porque finish toma todo el control del registro. 

Tenga en cuenta que, aunque los receptores de métodos son diferentes, las funciones no estáticas se denominan de la misma manera en el cuerpo principal. Rust permite la referencia y desreferenciación automática al llamar a métodos. Rust agrega automáticamente los cambios &, *, para que ese objeto coincida con la firma del método.

Ebook: Estos 4 patrones van a modernizar tu estrategia multinube

 Me llego este mail y quería compartirlo: 

Las empresas modernas eligen Oracle Cloud Infrastructure (OCI)

En este ebook te contamos las razones
Conoce cómo las empresas crean y ejecutan aplicaciones seguras y escalables gracias a la nube de Oracle.

martes, 29 de agosto de 2023

Metodos en Rust parte 2


Veamos este código: 

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}


impl Person {

    fn say_hello(&self) {

        println!("Hello, my name is {}", self.name);

    }

}


fn main() {

    let peter = Person {

        name: String::from("Peter"),

        age: 27,

    };

    peter.say_hello();

}

El &self del post anterior indica que el método toma prestado el objeto de forma inmutable. Hay otros posibles receptores para un método:

  • &self: toma prestado el objeto de tipo Person que llama utilizando una referencia compartida e inmutable. El objeto se puede volver a utilizar posteriormente.
  • &mut self: toma prestado el objeto de tipo Person que llama utilizando una referencia única y mutable. El objeto se puede volver a utilizar posteriormente.
  • self: toma posesión del objeto. El método se convierte en propietario del objeto. El objeto se eliminará (desasignará) cuando el método regrese, a menos que su propiedad se transmita explícitamente. La propiedad total no significa automáticamente mutabilidad.
  • mut self: igual que el anterior, pero el método puede mutar el objeto.
  • Sin receptor: esto se convierte en un método estático en la estructura. Normalmente se utiliza para crear constructores que se denominan new por convención.

Más allá de las variantes de self, también hay tipos de contenedores especiales que pueden ser tipos de receptores, como Box<Self>.

Estas restricciones siempre se juntan en Rust debido a las reglas del verificador prestado, y self no es una excepción. No es posible hacer referencia a una estructura desde múltiples ubicaciones y llamar a un método mutante (&mut self) en ella.

viernes, 25 de agosto de 2023

Metodos en Rust

 


Rust te permite asociar funciones con tus nuevos tipos. Hacemos esto con un bloque impl:

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}


impl Person {

    fn say_hello(&self) {

        println!("Hello, my name is {}", self.name);

    }

}


fn main() {

    let peter = Person {

        name: String::from("Peter"),

        age: 27,

    };

    peter.say_hello();

}

Los métodos se llaman en una instancia de un tipo (como una estructura o enumeración), el primer parámetro representa la instancia como self.

Los desarrolladores pueden optar por utilizar métodos para aprovechar la sintaxis del receptor de métodos y ayudar a mantenerlos más organizados. Al utilizar métodos, podemos mantener todo el código de implementación en un lugar predecible.

self es un término abreviado y es de tipo &Person, por lo tanto podemos escribir esto como : 

impl Person {

    fn say_hello(self : &Person) {

        println!("Hello, my name is {}", self.name);

    }

}


miércoles, 23 de agosto de 2023

Juego de Serie en Rust


Cuando quiero aprender un nuevo lenguaje desarrollo un juego de series, es decir aparece una serie con un valor faltante y el jugador debe completarlo.

Uno de los requerimientos no funcionales es que se pueda agregar una serie nueva fácilmente.

Vamos a desarrollar este juego en Rust:

Empecemos desarrollo de la serie, la serie tiene como responsabilidad generarse y si lo hacemos de esta manera podemos utilizar la potencia del polimorfismo, para que no quede acoplado la generación de la serie con el desarrollo del juego:

use rand::{thread_rng, Rng};


pub fn iniciar_secuencia() -> [u16;4] {

let semilla:u8 = thread_rng().gen_range(0..3);


match semilla {

    0 => return generar_secuencia_par(),

    1 => return generar_secuencia_inpar(),

        _ => return generar_fibonacci()

}

}

//Esta función genera la serie y para eso utiliza diferentes funciones que generan diferentes tipos de secuencias : 

fn generar_secuencia_par() -> [u16;4] {

let mut resultado:[u16;4] = [0; 4];

let semilla:u16 = thread_rng().gen_range(0..30);

for i in 0..4 {

resultado[i] = semilla * 2 + (i as u16) * 2;

}

resultado

}


fn generar_secuencia_inpar() -> [u16;4] {

let mut resultado:[u16;4] = [0; 4];

let semilla:u16 = thread_rng().gen_range(0..30);

for i in 0..4 {

resultado[i] = semilla * 2 + (i as u16) * 2 + 1;

}

resultado

}



fn generar_fibonacci() -> [u16;4] {

let mut _resultado:[u16;4] = [0; 4];

let semilla:u16 = thread_rng().gen_range(0..30);


for i in 0..4 {

if i < 2 {

_resultado[i] = semilla;

} else {

_resultado[i] = _resultado[i-1] + _resultado[i-2];

}

}

_resultado

}


Y listo!! ahora en el main tenemos que llamar a esta función y ver si esta bien el resultado: 


use std::io;


use crate::serie::iniciar_secuencia;


mod serie;


fn main() {

    let mut puntos = 0u8;

    loop {

        let serie:[u16;4] = iniciar_secuencia();


        println!("Serie: ");

        

        for i in 0..2 {

            print!(" {} ", serie[i]);

        }


        println!("_____ {} ", serie[3]);


        println!("Ingrese el valor faltante: ");

        let mut input = String::new();

        io::stdin().read_line(&mut input).expect("error: unable to read user input");

        let x = input.trim().parse().expect("Error!! debe ser un numero.");

        

        if serie[2].eq(&x) {

            println!("Ganaste !! ");

            puntos = puntos + 1;

        } else {

            println!("Perdiste !! ");

        }

        println!("Puntos :  {} ", puntos);

        println!("Desea continuar jugando? (y/n): ");

        let mut continuar = String::new();

        io::stdin().read_line(&mut continuar).expect("error: unable to read user input");

        

        if continuar.trim().ne("y"){

            return;

        }

    }    

}

Y eso es todo a jugar se a dicho!!

Dejo el repositorio git: https://github.com/emanuelpeg/secuenciaRust

lunes, 21 de agosto de 2023

Inscripciones Abiertas 2023 a cursos Gugler!!

 

PRENSA GUGLER  LAB 2023

  NOTICIAS ACTUALES

informacion  OFERTA ACADÉMICA


Estimado :

 Se encuentran abiertas las inscripciones para el segundo cuatrimestre del año 2023, para todas las capacitaciones dictadas por el Laboratorio de Investigación Gugler. Podés asegurar tu lugar en el curso y comisión que desees !!!.

Las clases inician:

  • Martes 12/09 , Miércoles 13/09, Jueves 14/09 o Sábado 16/09, según el curso que elegiste.

 

Inscribirteclic aquí

Cursos, Horarios y comisionesclic aquí.

 

 

Dictamos nuestros cursos en la Facultad de Ciencia y Tecnología, perteneciente a la Universidad Autónoma de Entre Ríos. En nuestro portafolio de capacitación encontrarás:

MODALIDAD PRESENCIAL

  • Cursos de Programación:
    Programación en PHP.
     
  • Cursos de Mantenimiento/Reparación:
    Reparación y Mantenimiento de PC.

 MODALIDAD DISTANCIA

  • Cursos de Programación:
    Programación Web Frontend.
    Programación en Java.
informacion   MÁS INFORMACIÓN 

informacion LABORATORIO DE INVESTIGACIÓN GUGLER

Si deseas comunicarte con nosotros, te recordamos que podes hacerlo a través de los siguientes portales, en los cuales encontrarás información sobre Gugler.

TEL: (0343) - 4975066 Interno 119

Sitio Oficial:  www.gugler.com.ar

Campus:  campusvirtual.gugler.com.ar

Sistema de Gestión Cursos:  sgc.gugler.com.ar

Sistema de Gestión Cursos Móvil: Aquí 

Sistema de Documentación:  sgd.gugler.com.ar

Sistema de Validación:  giua.gugler.com.ar

                   Twitter                Facebook

Laboratorio Gugler

partir del 2012, la Facultad de Ciencia y Tecnología nos declaro:  "Laboratorio de Investigación".

 

El laboratorio ha ampliado las incumbencias de Gugler, ya que además de la capacitación, la promoción y difusión del software libre, ahora abarcará actividades como publicaciones y proyectos de investigación, así como también el dictado y participación en conferencias o exposiciones de ámbitos académicos y científicos.

 

Ante cualquier duda comunicarse con nosotros a gugler_contacto@uader.edu.ar

GUGLER PRESS

Enums en Rust parte 3


Tamaños de enumeración:

use std::any::type_name;

use std::mem::{align_of, size_of};


fn dbg_size<T>() {

    println!("{}: size {} bytes, align: {} bytes",

        type_name::<T>(), size_of::<T>(), align_of::<T>());

}


enum Foo {

    A,

    B,

}


fn main() {

    dbg_size::<Foo>();

}


Internamente, Rust está utilizando un campo (discriminante) para realizar un seguimiento de la variante de enumeración.

Puede controlar el discriminante si es necesario (por ejemplo, para compatibilidad con C):

#[repr(u32)]
enum Bar {
    A,  // 0
    B = 10000,
    C,  // 10001
}

fn main() {
    println!("A: {}", Bar::A as u32);
    println!("B: {}", Bar::B as u32);
    println!("C: {}", Bar::C as u32);
}

Sin repr, el tipo discriminante ocupa 2 bytes, porque 10001 cabe en 2 bytes.

Pruebe otros tipos como

  • dbg_size!(bool): tamaño 1 byte, alineación: 1 byte,
  • dbg_size!(Opción<bool>): tamaño 1 bytes, alineación: 1 bytes (optimización de nicho, ver más abajo),
  • dbg_size!(&i32): tamaño 8 bytes, alineación: 8 bytes (en una máquina de 64 bits),
  • dbg_size!(Opción<&i32>): tamaño 8 bytes, alineación: 8 bytes (optimización de puntero nulo, ver más abajo).
Optimización de nicho: Rust fusionará patrones de bits no utilizados para el discriminante de enumeración.

Optimización de puntero nulo: para algunos tipos, Rust garantiza que size_of::<T>() es igual a size_of::<Option<T>>().

Código de ejemplo si desea mostrar cómo se vería la representación bit a bit en la práctica. Es importante tener en cuenta que el compilador no ofrece garantías con respecto a esta representación, por lo que es totalmente inseguro.

use std::mem::transmute;

macro_rules! dbg_bits {
    ($e:expr, $bit_type:ty) => {
        println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e));
    };
}

fn main() {
    // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise
    // representation of types.
    unsafe {
        println!("Bitwise representation of bool");
        dbg_bits!(false, u8);
        dbg_bits!(true, u8);

        println!("Bitwise representation of Option<bool>");
        dbg_bits!(None::<bool>, u8);
        dbg_bits!(Some(false), u8);
        dbg_bits!(Some(true), u8);

        println!("Bitwise representation of Option<Option<bool>>");
        dbg_bits!(Some(Some(false)), u8);
        dbg_bits!(Some(Some(true)), u8);
        dbg_bits!(Some(None::<bool>), u8);
        dbg_bits!(None::<Option<bool>>, u8);

        println!("Bitwise representation of Option<&i32>");
        dbg_bits!(None::<&i32>, usize);
        dbg_bits!(Some(&0i32), usize);
    }
}

Ejemplo más complejo si desea analizar qué sucede cuando encadenamos más de 256 opciones juntas.

#![recursion_limit = "1000"]

use std::mem::transmute;

macro_rules! dbg_bits {
    ($e:expr, $bit_type:ty) => {
        println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e));
    };
}

// Macro to wrap a value in 2^n Some() where n is the number of "@" signs.
// Increasing the recursion limit is required to evaluate this macro.
macro_rules! many_options {
    ($value:expr) => { Some($value) };
    ($value:expr, @) => {
        Some(Some($value))
    };
    ($value:expr, @ $($more:tt)+) => {
        many_options!(many_options!($value, $($more)+), $($more)+)
    };
}

fn main() {
    // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise
    // representation of types.
    unsafe {
        assert_eq!(many_options!(false), Some(false));
        assert_eq!(many_options!(false, @), Some(Some(false)));
        assert_eq!(many_options!(false, @@), Some(Some(Some(Some(false)))));

        println!("Bitwise representation of a chain of 128 Option's.");
        dbg_bits!(many_options!(false, @@@@@@@), u8);
        dbg_bits!(many_options!(true, @@@@@@@), u8);

        println!("Bitwise representation of a chain of 256 Option's.");
        dbg_bits!(many_options!(false, @@@@@@@@), u16);
        dbg_bits!(many_options!(true, @@@@@@@@), u16);

        println!("Bitwise representation of a chain of 257 Option's.");
        dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);
        dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);
        dbg_bits!(many_options!(None::<bool>, @@@@@@@@), u16);
    }
}

Enums en Rust parte 2


Ya vimos enums en Rust pero vamos a ver más característica: 


enum WebEvent {

    PageLoad,                 // Variant without payload

    KeyPress(char),           // Tuple struct variant

    Click { x: i64, y: i64 }, // Full struct variant

}


#[rustfmt::skip]

fn inspect(event: WebEvent) {

    match event {

        WebEvent::PageLoad       => println!("page loaded"),

        WebEvent::KeyPress(c)    => println!("pressed '{c}'"),

        WebEvent::Click { x, y } => println!("clicked at x={x}, y={y}"),

    }

}


fn main() {

    let load = WebEvent::PageLoad;

    let press = WebEvent::KeyPress('x');

    let click = WebEvent::Click { x: 20, y: 80 };


    inspect(load);

    inspect(press);

    inspect(click);

}


  • Solo se puede acceder a los valores en las variantes de enumeración después de hacer pattern matching. E
    • La expresión se compara con los patrones de arriba a abajo. 
    • Comenza desde arriba, buscando qué patrón coincide con el valor y luego ejecuta el código que se encuentra a la derecha de la flecha. 
  • Tenga en cuenta la ventaja que proporciona el compilador de Rust al confirmar cuándo se manejan todos los casos.
  • match inspecciona los campos publicos de  la enumeración.
  • Es posible recuperar el discriminante llamando a std::mem::discriminant(). Esto es útil, por ejemplo, si se implementa PartialEq para estructuras donde comparar valores de campo no afecta la igualdad.
  • WebEvent::Click { ... } no es exactamente lo mismo que WebEvent::Click(Click) con una estructura de nivel superior Click { ... }. La versión en línea no puede implementar rasgos, por ejemplo.

martes, 15 de agosto de 2023

Enums en Rust


La palabra clave enum permite la creación de un tipo que tiene algunas variantes diferentes:

fn generate_random_number() -> i32 {

    // Implementation based on https://xkcd.com/221/

    4  // Chosen by fair dice roll. Guaranteed to be random.

}


#[derive(Debug)]

enum CoinFlip {

    Heads,

    Tails,

}


fn flip_coin() -> CoinFlip {

    let random_number = generate_random_number();

    if random_number % 2 == 0 {

        return CoinFlip::Heads;

    } else {

        return CoinFlip::Tails;

    }

}


fn main() {

    println!("You got: {:?}", flip_coin());

}


  • Las enumeraciones le permiten recopilar un conjunto de valores bajo un tipo
  • Este ejemplo ofrece un tipo de enumeración CoinFlip con dos variantes Cara y Cruz. (CoinFlip::Heads y CoinFlip::Tails)
  • Este podría ser un buen momento para comparar Structs y Enums:
    • En ambos, puede tener una versión simple sin campos (estructura de unidad) o una con diferentes tipos de campos (cargas de variantes).
    • En ambos, las funciones asociadas se definen dentro de un bloque impl.
    • Incluso podría implementar las diferentes variantes de una enumeración con estructuras separadas, pero entonces no serían del mismo tipo que si estuvieran todas definidas en una enumeración.

lunes, 14 de agosto de 2023

Constructor en Rust

 


Si ya tiene variables con los nombres correctos, puede crear la estructura usando una abreviatura:

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}


impl Person {

    fn new(name: String, age: u8) -> Person {

        Person { name, age }

    }

}


fn main() {

    let peter = Person::new(String::from("Peter"), 27);

    println!("{peter:?}");

}


Podría escribirse usando Self como tipo, ya que es intercambiable con el nombre del tipo de estructura

#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}

impl Person {

    fn new(name: String, age: u8) -> Self {

        Self { name, age }

    }

}

Podemos tambien definir los campos por defecto: 


#[derive(Debug)]

struct Person {

    name: String,

    age: u8,

}

impl Default for Person {

    fn default() -> Person {

        Person {

            name: "Bot".to_string(),

            age: 0,

        }

    }

}

fn create_default() {

    let tmp = Person {

        ..Person::default()

    };

    let tmp = Person {

        name: "Sam".to_string(),

        ..Person::default()

    };

}


  • Los métodos se definen en el bloque impl.


sábado, 12 de agosto de 2023

Tuple Structs en Rust


Si los nombres de los campos no son importantes, puede usar una estructura de tupla:


struct Point(i32, i32);

fn main() {

    let p = Point(17, 23);

    println!("({}, {})", p.0, p.1);

}


Esto se usa a menudo para envoltorios de un solo campo:


struct PoundsOfForce(f64);

struct Newtons(f64);


fn compute_thruster_force() -> PoundsOfForce {

    todo!("Ask a rocket scientist at NASA")

}


fn set_thruster_force(force: Newtons) {

    // ...

}


fn main() {

    let force = compute_thruster_force();

    set_thruster_force(force);

}

Los tipos nuevos son una excelente manera de codificar información adicional sobre el valor en un tipo primitivo, por ejemplo:
  • El número se mide en algunas unidades: Newtons en el ejemplo anterior.
  • El valor pasó cierta validación cuando se creó, por lo que ya no tendrá que volver a validarlo cada vez que lo use