Interacción con el entorno y datos del usuario
Clase 19 de 20 • Curso de Rust básico
Contenido del curso
Clase 19 de 20 • Curso de Rust básico
Contenido del curso
Alex Paul Chirino Caicedo
Brandon Manzo
Erick Tucto
Romel Manrique
Hecot Pulido
Sergio Estrella
Eber Laurente Lliuyacc
Hecot Pulido
Luis Suárez
Víctor Andrés Córdova Poma
Víctor Andrés Córdova Poma
Sebastian Torrejon Rubio
Leandro Ariel Labiano Ramo
José Ordoñez
Mauricio Sebastian Gutierrez Perdomo
José Pinto
Simon Jimenez
David Rangel
Willy Samuel Paz Colque
Luis Suárez
Me parece un curso muy bueno ya que explica varios conceptos (no solo de Rust sino de programación a más bajo nivel) de una manera didáctica y con proyectos como este que hacen sentir al estudiante los dominios básicos del lenguaje.
Sin embargo, creo que se quedó corto en muchos otros aspectos como:
impl y structuse y mod que son las maneras en que los proyectos se organizan en Rust (a mí parecer es básico saber de un lenguaje la organización del código)match que es algo diferente de Rust como lenguaje y que está relacionado con tanto uso de .unwrap()Me gustó mucho el curso, está entrenido y práctico, pero si se esperaba que el estudiante llegara a realizar un proyecto como este, pienso que como mínimo se debieron explicar mejor la mayoría de los conceptos que se iban a utilizar con anterioridad.
Yo desconozco totalmente Rust, de hecho he llevado otros cursos como dart y swift aquí mismo en platzi y desglosan bastante todos los aspectos de los lenguajes de programación, pero tomando estos cursos de ejemplo, siento como si hace falta algo, por ejemplo: los float, el ciclo while, no se si existe switch aquí u alguna explicación de otros tipos de datos, entre otras cosas que esperaba encontrar.
Personalmente en este curso aprendi algo básico; funciones, string e int, arreglos/vectores y ciclos, no esta mal, pero aún me quedo con la misma sensación, siento que hizo falta un poquito mas.
si, falto explicar varias cosas, por ejemplo pasar por referencia y valor, para que es el asterisco, que diferencia hay entre let y let mut y const, en la pagina oficial de rust hay un handbook, donde se puede entender varias cosas que se dejaron en el curso
Investigué un poco sobre una parte del código que no entendia.
if let Some(data) = datos_historia.get_mut(&last_record)
en la evaluación de la condición estamos comprobando si la variable data es verdadera o falsa, sin embargo en este caso, ya que el método .get_mut puede devolverno un valor None (que en condicionales es equivalente a false) ya que last_record, puede no estar en nuestro diccionario.
(*data).opciones.push(dato);
en esta parte estamos "desempaquetando" el valor que se encuentra en la dirección de memoria que nos devuelve el método .get_mut. Como este método devuelve un pointer (de un objeto de tipo DatoHistoria) no podemos aplicarle los métodos o atributos propios del tipo al que pertenece. Por eso utilizamos (*data).opciones.push(tag_actual).
En C, está sintaxis es parecida o se puede aplicar: data->opciones.push(tag_actual)
Otra forma en la que se puede escribir esta seccion es asi:
let data : Option<&mut DatoHistoria> = datos_historia.get_mut(&last_record); match data { Some(data) => data.opciones.push(dato), None => {continue;}, }
En este caso ya no necesitamos "desempaquetar" el valor de pointer porque le pedimos una referencia al metodo, en lugar de un pointer.
Muy buena investigación, muchas gracias por mejorar la información 😌
Por acá les dejo el repositorio del juego hecho en Rust, ha sido una grandiosa experiencia :D
Muy denso para alguien como yo que viene de aprender Python, pero muy emocionante a la vez. Habrá que reforzar con lecturas externas para entender mejor 💪🏽
Me alegro que te guste 😌
Python no es comparable con Rust. No porque sea mejor o peor simplemente son puntos de vistas muy diferentes. De todas maneras creo que como un inicio en RUST no está mal.
Debo reconocer que Rust es bastante mas complejo como para que en 20 clases nos enteremos de 10% de lo que se puede llegar hacer con rust. Es algo quizás que se debe complementar con libros... No queda otra.
Quiero aportar mi código donde hice unos cambios, como agregar íconos, hice más funcional el código, y que la vida sea de tipo vector que contiene char 🧡, para que se vea mejor. Además estuve usando enum y referencias. Si quieren darle un ojo aquí está el repo.
Por medio de las funciones lo hice más fácil de leer.
pub fn game() { let mut current_tag = FIRST_TAG.to_owned(); let mut last_tag = String::new(); let mut input = String::new(); let mut selected_index: usize; let arrows: [char; 3] = ['↗', '➡', '↘']; let mut health_vec = Vec::from(['🧡', '🧡']); let content_result: Result<String, Error> = fs::read_to_string(FILE_NAME); let mut data: HashMap<String, Row> = HashMap::new(); match content_result { Ok(content) => { let mut reader_result = ReaderBuilder::new(). delimiter(b';'). from_reader(content.as_bytes()); add_data(&mut reader_result, &mut data, &mut last_tag); loop { let situation_op = data.get(¤t_tag); match situation_op { Some(situation) => { update_health(&mut health_vec, &situation.health); show_situation(&situation.text, &health_vec); show_options(&situation.options, &arrows); if health_vec[0] == '🖤' { println!("Haz perdido."); break; } stdin().read_line(&mut input).unwrap(); println!(""); selected_index = input.trim().parse().unwrap_or(99); choose_path(&situation.options, selected_index, &mut current_tag); input.clear(); }, None => { println!("Ganaste!!!"); break; }, } } }, Err(error) => println!("Error: {error}"), } }
Me hubiese gustado que el curso fuera un poco mas largo y asi poder explicar y definir de mejor manera las particularidades de este lenguaje.
Gran forma de aprender estructura de datos en rust
Aquí explican bien la distinción entre la referencia (reference) y el prestar (borrow) dentro del scope que se está ejecutando la función.
Ah, entonces el problema de que el tag sobrescribiera la entrada de hashmap en el codigo de la clase anterior solo fue que estaba incompleto.
Bueno, al final de esta el juego tiene un problema (ejecute tambien el propio archivo de la clase para asegurarme de que ese ahi estaba), si una entrada es invalida, el bucle vuelve a sumar o restar vida si se encuentra en esa parte.
Para arreglar esto, solo agrege un booleano is_frist_selection , se comprueba antes de sumar la vida, se pone false cuando la entrada es invalida y vuelve a true cuando es correcta.
use std::io::{self, Write}; use std::{collections::HashMap, fs}; use csv::{ReaderBuilder,StringRecord}; const PATH : &str = "history.csv"; const FRIST_TAG: &str = "INICIO"; fn get_input() ->io::Result<String>{ let mut input = String::new(); io::stdout().flush()?; io::stdin().read_line(&mut input)?; Ok(input) } #[derive(Debug, PartialEq)] struct StoryLine { data_type : String, tag : String, text : String, life : i32, options : Vec<StoryLine> } impl StoryLine { fn new(row: StringRecord) -> StoryLine { StoryLine { data_type : row.get(0).unwrap().trim().to_string(), tag: row.get(1).unwrap().trim().to_string(), text: row.get(2).unwrap().trim().to_string(), life: row.get(3).unwrap().trim().to_string().parse().unwrap_or(0), options: Vec::new() } } } fn main() { game(get_lines_form_path(PATH)); } fn get_lines_form_path(path: &str) -> HashMap<String, StoryLine>{ // let mut no_used_lines : Vec<StoryLine> = Vec::new(); let mut lines : HashMap<String, StoryLine> = HashMap::new(); let content = fs::read_to_string(path).unwrap(); let mut rbr = ReaderBuilder::new().delimiter(b';').from_reader(content.as_bytes()); let mut last_line: String = String::default(); for line in rbr.records(){ let line = line.unwrap(); let new_line = StoryLine::new(line); if new_line.data_type == "SITUACION" { let tag = new_line.tag.clone(); lines.insert(tag.clone(), new_line); last_line = tag }else if new_line.data_type == "OPCION" { if let Some(situacion) = lines.get_mut(&last_line){ situacion.options.push(new_line); } // else {no_used_lines.push(new_line);} } } lines } fn game(lines: HashMap<String, StoryLine>){ let mut life : i32 = 100; let mut current_tag: String = FRIST_TAG.to_string(); let mut is_just_selection: bool = true; loop { if life <=0 { println!("Fin del juego"); break; } if let Some(line) = lines.get(¤t_tag){ if is_just_selection{life += line.life} //si una entrada es invalida, se vuelve a ejecutar esto println!("Vida: {}",life); println!("{}", line.text); for (index,option) in line.options.iter().enumerate(){ println!("{{{}}}{}", index, option.text) } let chosen : usize = get_input().unwrap().trim().parse().unwrap_or(usize::max_value()); if let Some(chose_option) = line.options.get(chosen){ is_just_selection = true; current_tag = chose_option.tag.clone(); } else { println!("Entrada invalida"); is_just_selection = false } println!("") } else {break;} } }
Muy bueno el curso, pero la verdad no me queda muy claro para que usar el comando clone en este contexto.
La verdad, ha sido un curso bastante pobre en cuanto a explicaciones a profundidad del uso del lenguaje.
Recuerden que para romper un loop que no tiene break usen: CTR + C
bueno separe un poco el codigo en funciones y agregue un poco de estilo al texto
Codigo: Samuelpaz95
Aqui estan mis resultados
Bien en el capitulo anterior quedamos con:::
use csv::{ReaderBuilder, StringRecord}; use std::collections::{HashMap}; // Sip algo parecido a un diccionario de python pero nada que ver ja! use std::{fs}; //fs file system const FILENAME: &str = "history.csv"; // TIPO, TAG, TEXTO , VIDA #[derive(Debug)] struct DatoHistoria { d_tipo: String, d_tag: String, d_txt: String, d_vida: i32 } // Implementamos una funcion dentro de DatoHistoria impl DatoHistoria{ // creamos la funcion y le pasamos el argumento fila del tipo StringRecord y retornamos un -> DatoHistoria fn new(row: StringRecord) -> DatoHistoria{ // Como todo en la vida es complicado debemos pasar de un texto a un numerito del tipo i32 let vida = row.get(3).unwrap().trim(); // Le hacemos unas magias. // unwrap_or() es igual que unwrap() pero si detecta un error retorna lo que le pasemos en este caso un 0 let vida : i32 = vida.parse().unwrap_or(0); // Y aca le decimos mirá si no tienes nada y como yo nulo no tengo metele un cero total por ahora estas aprendiendo. // Retornamos todo lo que viene del argumento row ... si row es fila pero en ingles suena mas bonito. Bueno espaenglish porque vida es life... bueno tu me entiendes // Por experiencia dejemos funciones y variables en español y aprovechemos los dos idiomas ya que cuando trabajas mentira que haces todo en ingles. return DatoHistoria { d_tipo: row.get(0).unwrap().trim().to_string(), d_tag: row.get(1).unwrap().trim().to_string(), d_txt: row.get(2).unwrap().trim().to_string(), d_vida: vida, }; } } fn main() { // Crea el vector // let mut dato_historia: Vec<DatoHistoria> = vec![]; let mut dato_historia: HashMap<String, DatoHistoria> = HashMap::new(); // Carga el contenido del archivo csv let content = fs::read_to_string(FILENAME).unwrap(); //lo asigna a una varible en bites separandolo por ; est va a generar un vector. let mut rdr = ReaderBuilder::new().delimiter(b';').from_reader(content.as_bytes()); //Recorremos el vector for result in rdr.records() { //asigna la variable resultado pasando unwrap() #unwrap nos dara un detalle del error // Se puede mejorar la salida del error con .expect("Ha ocurrido algo!!!"); let r = result.unwrap(); // llama a la funcion new que podría llamarse cargar_datos let dato = DatoHistoria::new(r); // agregamos datos a dato_historia al mapa // IMPORTANTE: Esto puede costar recursos... // Se diferencia de Copiar en que Copiar es una copia implícita y económica en términos de bits, mientras que Clonar siempre es explícito y puede o no ser costoso. dato_historia.insert(dato.d_tag.clone(), dato); // agregamos datos a dato_historia al vector // dato_historia.push(dato); } println!("{:?}", dato_historia);