1.6 Lo que una tarea sabe de sí misma
Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.
Un nombre no es una habilidad. La tarea lleva un nombre, conserva un estado y puede describirse. Pero el conocimiento de cómo trabajar con ella vive fuera - con el artesano. Mientras hay una sola pieza y el artesano no está cansado, eso es tolerable. Lo tolerable tiene la costumbre de volverse insoportable en el momento menos conveniente.
Lo que necesitas
Tras el capítulo anterior, la tarea tiene dos ayudantes: create_task la crea,
task_complete la completa. Es un paso adelante: las operaciones tienen nombres, el
conocimiento sobre los campos no está disperso por main.
Pero ambas funciones conocen la estructura interna de la tarea - sus campos, Status::Todo
y Status::Done. Ese conocimiento vive fuera del tipo. Con dos funciones, es tolerable.
Cada nueva función lo duplicará por su cuenta.
En Rust, el comportamiento de un tipo vive en un bloque impl. Dale a la tarea tres
capacidades: crearse a sí misma, completarse y responder una pregunta sobre su estado.
create_task se convierte en Task::new, y task_complete se convierte en el método
complete.
La construcción
create_task y task_complete ya no son necesarias - su lógica se traslada a impl Task.
Añade el bloque inmediatamente después de struct Task:
impl Task {
fn new(id: u64, title: &str) -> Task {
Task {
id,
title: String::from(title),
status: Status::Todo,
}
}
}
impl Task abre el bloque donde se define el comportamiento de Task. new es una función
sin parámetro self: no trabaja con una tarea existente, crea una nueva. Se llama con ::,
no con un punto: Task::new(1, "Buy coffee").
Reemplaza create_task en main:
let mut task = Task::new(1, "Buy coffee");
let mut - la tarea cambiará en el siguiente paso; esto es familiar del capítulo anterior.
Añade el método complete al mismo bloque impl Task:
fn complete(&mut self) {
self.status = Status::Done;
}
complete toma &mut self - modifica la tarea, así que let mut en el lugar de llamada
es necesario. Ya está ahí. La llamada: task.complete().
Y un método más - is_done:
fn is_done(&self) -> bool {
match self.status {
Status::Done => true,
Status::Todo => false,
}
}
is_done toma &self - solo lectura. El match es familiar: dos variantes, dos respuestas.
El resultado
#[derive(Debug)]
enum Status {
Todo,
Done,
}
#[derive(Debug)]
struct Task {
id: u64,
title: String,
status: Status,
}
impl Task {
fn new(id: u64, title: &str) -> Task {
Task {
id,
title: String::from(title),
status: Status::Todo,
}
}
fn complete(&mut self) {
self.status = Status::Done;
}
fn is_done(&self) -> bool {
match self.status {
Status::Done => true,
Status::Todo => false,
}
}
}
fn main() {
let mut task = Task::new(1, "Buy coffee");
println!("Task #{}: {}", task.id, task.title);
println!("{:#?}", task);
task.complete();
println!("{:#?}", task);
if task.is_done() {
println!("done");
} else {
println!("todo");
}
}
Task #1: Buy coffee
Task {
id: 1,
title: "Buy coffee",
status: Todo,
}
Task {
id: 1,
title: "Buy coffee",
status: Done,
}
done
make ci pasa. Tres métodos listos - el capítulo 1.11 añadirá tests para ellos.
El código completo de
tqpara este capítulo está en1-a-task/06-what-a-task-knows-of-itself/.
Lore: &self, &mut self y self
En el capítulo anterior las mismas tres formas eran Task, &Task, &mut Task - en los
métodos se convierten en self, &self, &mut self.
Los métodos en Rust tienen tres formas de recibirse como primer parámetro:
&self- préstamo compartido. El valor no se modifica; el llamador lo conserva.&mut self- préstamo mutable. El método puede modificar el valor; el llamador debe tenerlet mut.self- transferencia de propiedad. El método consume el valor; tras la llamada ya no existe. Poco frecuente, pero aparece - por ejemplo, en métodos que convierten un tipo en otro.
new no toma ningún self: no es un método sino una función asociada. Pertenece al tipo,
no a una instancia - de ahí Task::new, no task.new.
Lore: Macros - una introducción lejana
En código real, is_done se escribe a menudo de otra forma:
fn is_done(&self) -> bool {
matches!(self.status, Status::Done)
}
matches! es una macro: un nombre con signo de exclamación. Las macros en Rust son un tema
amplio en sí mismo - habrá más sobre ellas más adelante. Por ahora, una cosa es suficiente:
un signo de exclamación tras un nombre significa una macro, no una función regular. matches!
aquí hace exactamente lo que hace el match - solo que más corto.