4.1 El lenguaje de las máquinas
Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.
Un buen artesano lleva registros. Un artesano deficiente confía en la memoria - y eso funciona bien hasta que tres pedidos han desaparecido y hay que explicarlos. La memoria es cómoda: siempre está a mano y siempre, por regla, ya es ligeramente diferente. Los registros son incómodos: hay que llevarlos. Pero solo uno de los dos sobrevive a una puerta cerrada.
Lo que necesitas
tq puede añadir tareas, completarlas y encontrarlas por identificador. Cada vez - desde
cero. Todo lo añadido en la ejecución anterior desapareció con ella. tq no tiene registros.
Para guardar tareas en un archivo, deben traducirse a un formato de texto. JSON encaja: es
legible por personas, legible por máquinas, y la mayoría de herramientas lo entienden. La
herramienta de traducción - serde - ya está en su lugar. Lo que queda es decirle a Task
cómo usarla.
El vocabulario
En src/task/mod.rs, añade la importación y los atributos a Status y Task:
// src/task/mod.rs - CAMBIADO: importación de serde; derives en Status y Task
use crate::error::{TqError, TqResult};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Serialize, Deserialize)]
pub enum Status {
Todo,
Done,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Task {
pub id: u64,
pub title: String,
pub status: Status,
}
#[derive(Serialize, Deserialize)] es necesario en Status además de en Task: serde
sabe manejar tipos estándar - números, cadenas - pero Status es un tipo propio, y sin
instrucción explícita no puede serializarlo.
Añade una línea en src/main.rs para confirmar que la traducción funciona:
// src/main.rs - añadir antes de Ok(())
let json = serde_json::to_string_pretty(store.get(1)?).unwrap();
println!("as JSON:\n{}", json);
El .unwrap() aquí es temporal: el tipo de error de serde_json todavía no está conectado
a TqResult.
El resultado
#1: Buy coffee [Todo]
#2: Buy milk [Todo]
#3: Buy eggs [Todo]
found: #1: Buy coffee [Todo]
task 99 not found
done: true
rejected: task title cannot be empty
as JSON:
{
"id": 1,
"title": "Buy coffee",
"status": "Done"
}
Dos nuevos tests en src/task/tests.rs - comprueban la traducción en ambas direcciones:
// src/task/tests.rs - añadir al final
#[test]
fn task_serializes_to_json() {
let task = Task::new(1, "Buy coffee").unwrap();
let json = serde_json::to_string(&task).unwrap();
assert!(json.contains(r#""title":"Buy coffee""#));
assert!(json.contains(r#""status":"Todo""#));
}
#[test]
fn task_roundtrips_through_json() {
let task = Task::new(1, "Buy coffee").unwrap();
let json = serde_json::to_string(&task).unwrap();
let restored: Task = serde_json::from_str(&json).unwrap();
assert_eq!(restored.id, task.id);
assert_eq!(restored.title, task.title);
assert!(!restored.is_done());
}
make ci pasa. Escribir en disco es el siguiente paso.
El código completo de
tqpara este capítulo está en4-memory/01-the-language-of-machines/.
Lore: Macros procedurales
#[derive(Debug)] y #[derive(Clone)] están integrados en el compilador - no se necesita
ningún crate. #[derive(Serialize, Deserialize)] es un caso diferente: esta es una macro
procedural que viene del propio crate serde. Eso es exactamente lo que activa la opción
--features derive del capítulo anterior.
En tiempo de compilación, lee la definición del tipo y genera una implementación del trait
Serialize - la misma que podrías haber escrito a mano. No es magia - es una herramienta
que escribe código por ti.
¿Por qué una feature separada? Compilar macros procedurales añade tiempo de compilación. Un
crate que usa serde solo como un conjunto de traits - en una biblioteca, no en un binario -
puede prescindir de la generación de código. La opción significa que no pagas por lo que no
usas.