Quiero recomendarles el tutorial tryerlang que encontre de casualidad. Si bien es basico, es muy interactivo y esta bueno.
Dejo link: https://www.tryerlang.org/
Dejo link: https://www.tryerlang.org/
#[derive(Debug)]
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
fn main() {
let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");
}
Si no se usaba Box e intentábamos incrustar una Lista directamente en la Lista, el compilador no calcularía un tamaño fijo de la estructura en la memoria (la Lista sería de tamaño infinito).
Box resuelve este problema ya que tiene el mismo tamaño que un puntero normal y solo apunta al siguiente elemento de la Lista.
Veamos que pasa si lo eliminamos:
enum List<T> {
Cons(T, List<T>),
Nil,
}
fn main() {
let list: List<i32> = List::Cons(1, List::Cons(2, List::Nil));
println!("{list:?}");
}
Y si ejecutamos esto:
cargo run
Compiling hello_cargo v0.1.0
error[E0072]: recursive type `List` has infinite size
--> src/main.rs:2:1
|
2 | enum List<T> {
| ^^^^^^^^^^^^
3 | Cons(T, List<T>),
| ------- recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
|
3 | Cons(T, Box<List<T>>),
| ++++ +
For more information about this error, try `rustc --explain E0072`.
error: could not compile `hello_cargo` due to previous error
Un Box no puede estar vacío, por lo que el puntero siempre es válido y no nulo. Esto permite al compilador optimizar el diseño de la memoria.
Bueno, todo tiene un principio y vamos a empezar a crear un proyectito de consola con Clojure y Leiningen
$ lein new app my-project
Fijate vos que si queres utilizar nombre de proyecto con mayuscula, no te deja, ojo ahi.
Ahora vamos a ejecutarlo :
$ lein run
podemos generar un jar con :
$ lein jar
Si estamos trabajando en un proyecto de aplicación, Leiningen nos brinda la posibilidad de construir lo que se llama un uberjar. Este es un archivo JAR que contiene el proyecto en sí y todas las dependencias y está configurado para permitir que se ejecute tal cual.
$ lein uberjar
Compiling secuencia-clojure.core
$ java -jar target/uberjar/el-jar-standalone.jar
fn main() {
let five = Box::new(5);
println!("five: {}", *five);
}
Box<T> implementa Deref<Target = T>, lo que significa que puedes llamar métodos desde T directamente en un Box<T>.
Box es como std::unique_ptr en C++, excepto que se garantiza que el elemento no será nulo.
En el ejemplo anterior, incluso podemos omitir el * en println! gracias a Deref.
Un Box puede resultar útil cuando:
use std::collections::HashMap;
fn main() {
let mut page_counts = HashMap::new();
page_counts.insert("Adventures of Huckleberry Finn".to_string(), 207);
page_counts.insert("Grimms' Fairy Tales".to_string(), 751);
page_counts.insert("Pride and Prejudice".to_string(), 303);
if !page_counts.contains_key("Les Misérables") {
println!("We know about {} books, but not Les Misérables.",
page_counts.len());
}
for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
match page_counts.get(book) {
Some(count) => println!("{book}: {count} pages"),
None => println!("{book} is unknown.")
}
}
// Use the .entry() method to insert a value if nothing is found.
for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
let page_count: &mut i32 = page_counts.entry(book.to_string()).or_insert(0);
*page_count += 1;
}
println!("{page_counts:#?}");
}
Si ejecutamos este programa:
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/hello_cargo`
We know about 3 books, but not Les Misérables.
Pride and Prejudice: 303 pages
Alice's Adventure in Wonderland is unknown.
{
"Pride and Prejudice": 304,
"Grimms' Fairy Tales": 751,
"Adventures of Huckleberry Finn": 207,
"Alice's Adventure in Wonderland": 1,
}
HashMap no está definido por defecto y es necesario incluirlo : use std::collections::HashMap;
Desde Rust 1.56, HashMap implementa From<[(K, V); N]>, que nos permite inicializar fácilmente un mapa hash a partir de una matriz literal:
let page_counts = HashMap::from([
("Harry Potter and the Sorcerer's Stone".to_string(), 336),
("The Hunger Games".to_string(), 374),
]);
Alternativamente, HashMap se puede construir a partir de cualquier iterador que produzca tuplas de valores clave.
Mostramos HashMap<String, i32> y evitamos usar &str como clave para facilitar los ejemplos. Por supuesto, se puede utilizar referencias en colecciones, pero puede generar complicaciones con el verificador de préstamos.
Podemos agregar estas lineas :
let pc1 = page_counts
.get("Harry Potter and the Sorcerer's Stone ")
.unwrap_or(&336);
let pc2 = page_counts
.entry("The Hunger Games".to_string())
.or_insert(374);
La primera línea verá si un libro está en el HashMap y, en caso contrario, devolverá un valor alternativo. La segunda línea insertará el valor alternativo en el HashMap si no se encuentra el libro.
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.
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.
Los tipos de vocabulario comunes incluyen:
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.
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.
#[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.
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. |
#[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:
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.
#[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);
}
}
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;
}
}
}
PRENSA GUGLER LAB 2023 | |||||
NOTICIAS ACTUALES | OFERTA ACADÉMICA | ||||
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:
Inscribirte: clic aquí Cursos, Horarios y comisiones: clic 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
MODALIDAD DISTANCIA
| ||||
MÁS INFORMACIÓN | 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 |
| ||||
GUGLER PRESS | |||||