Ir al contenido

Diario

  • Lección 1 — Cadena de herramientas y Cargo
  • Lección 2 — Tipos, mutabilidad y expresiones
  • Lección 3 — Ownership y movimientos
  • Lección 4 — Préstamos y cadenas
  • Lección 5 — Structs, enums y pattern matching
  • Lección 6 — Option, Result y ?
  • Lección 7 — Traits y genéricos
  • Lección 8 — Colecciones e iteradores
  • Lección 9 — Tiempos de vida (lifetimes)
  • Lección 10 — Módulos, crates y workspaces
  • Lección 11 — Box, Rc, Arc, RefCell
  • Lección 12 — Hilos, Send/Sync, Mutex, rayon
  • Lección 13 — async y tokio
  • Lección 14 — Tests, docs, clippy, fmt
  • Lección 15 — Macros, unsafe y FFI
  • Cadena de herramientas en mi máquina: rustc 1.94.0, cargo 1.94.0, edición 2024 por defecto para cargo new.
  • Cada fragmento se compiló antes de escribirse en una lección; los errores del compilador están copiados de la salida real de rustc (solo se recortaron las largas notas «other types implement this trait»).
  • El código del curso vive en code/rust-for-csharp-java: examples/ ejecutables y doctests compile_fail en src/lib.rs, verificados por el workflow de GitHub Rust course examples.

Sorpresas viniendo de C#:

  • El desbordamiento de enteros provoca un pánico en las compilaciones de depuración, pero da la vuelta en las compilaciones release — verificado con 255u8 + 1 compilado de las dos maneras. C# da la vuelta en ambos casos salvo con checked.
  • Clippy marcó let scores = vec![90, 72, 85, 60]; como useless_vec porque el vector solo se leía como slice — bastaba con un array.
  • El error del borrow checker para «push mientras se itera» es la versión en tiempo de compilación de InvalidOperationException: Collection was modified.

Resuelto: ¿comprueba compile_fail,E0382 el código de error? No en stable. Un doctest marcado compile_fail,E0999 alrededor de un uso tras un movimiento (en realidad E0382) seguía pasando con cargo test --doc en 1.94.0 — stable solo comprueba que la compilación falla. El workflow de CI ahora también ejecuta cargo +nightly test --doc, que sí compara los códigos.

Cada solución de ejercicio es ahora también un doctest: 41 doctests en total (fragmentos compile-fail más soluciones), todos en verde en 1.94.0.

Errores que los tests detectaron antes de publicar:

  • Primero escribí prices.sort_by(|a, b| a.total_cmp(b)) sobre vec![19.99, 5.0, 12.5]. No compila: los literales siguen siendo un {float} sin decidir cuando se comprueban los tipos de la closure (E0599: no method named total_cmp found for reference &{float}). Anotar Vec<f64> lo arregla — la lección 8 lo explica ahora.
  • En la lección 7 afirmé que cheapest podía recibir un Vec<Box<dyn Priced>> con una restricción T: Priced + ?Sized. Falso: &[T] exige T: Sized. La versión que funciona implementa Priced para Box<dyn Priced>, y eso es lo que el ejercicio muestra (y prueba) ahora.
  • Clippy rechazó (1..=5).fold(1, |acc, n| acc * n) en favor de .product() (unnecessary_fold), así que el ejemplo de fold calcula un par mín/máx — algo que ningún adaptador hace por sí solo.

Sorpresas viniendo de C#:

  • Cuando main devuelve Err, Rust imprime el error con su formato Debug (Error: Missing("port")), no Display, y termina con el código 1.
  • Vec<f64>::sort() no compila en absoluto (f64 no es Ord), mientras que C# y Java ordenan doubles sin rechistar.
  • El mensaje E0004 para una nueva variante de enum nombra el patrón exacto que falta (&Payment::Crypto { .. } not covered) — mejor que cualquier advertencia de analizador de C# que conozca.

76 doctests ahora (stable y nightly), más un workspace real de dos crates para la lección 10 (code/rust-for-csharp-java/l10-workspace) que la CI prueba, analiza y ejecuta. El crate del curso ganó su primera dependencia: rayon, como dev-dependency.

Cosas que primero hice mal:

  • En el ejemplo de la lección 9 usé drop(parser) para mostrar que los tokens sobreviven al parser. Clippy lo rechazó (drop_non_drop): hacer drop de un tipo sin impl de Drop no sirve de nada. Una función tokenize cuyo parser local muere al final demuestra lo mismo mejor.
  • Clippy también me enseñó u64::is_multiple_of (manual_is_multiple_of) en lugar de n % d != 0.
  • Supuse que un map de rayon que muta un contador capturado fallaría con un error de Send/Sync. Falla antes: las closures de rayon son Fn, así que es E0594: cannot assign to a captured variable in a Fn closure.

Sorpresas:

  • El mensaje de pánico de RefCell en 1.94 es simplemente RefCell already borrowed; el material más antiguo cita already borrowed: BorrowMutError.
  • Escribir &str en lugar de &'a str como tipo de retorno de un método compila sin problema — el error solo aparece en el punto de llamada, cuando intentas conservar dos tokens (E0499). La elisión eligió el lifetime de &mut self.
  • Un enum recursivo sin Box da E0391 (un ciclo en la consulta «needs drop» del compilador) además de E0072.
  • rayon en esta máquina (Core Ultra 9 285K, 24 núcleos): contar los primos por debajo de 5.000.000 pasó de ~775 ms a ~41 ms, unas 18×.

2026-09-13 — Lecciones 13 a 15, y tres sistemas operativos

Sección titulada «2026-09-13 — Lecciones 13 a 15, y tres sistemas operativos»

El curso principal está completo. El código tiene ahora tokio como dev-dependency, dos crates pequeños más (l14-testing, y l15-ffi con un cliente .NET 10), y la CI lo ejecuta todo en Windows, Ubuntu y macOS. El programa C# llama con éxito a la biblioteca Rust en los tres.

Cosas que primero hice mal:

  • El test FFI de la lección 15 afirmaba pricing_sum([19.99, 5.0, 12.5]) == 37.49. La suma f64 real es 37.489999999999995; C# había impreso 37.49 solo por su formato F2.
  • Un comentario XML del .csproj contenía --release: MSBuild se niega a cargar un proyecto cuyo comentario contiene --.
  • Escribí que [LibraryImport] serializa bool como un BOOL de 4 bytes por defecto. No tiene ningún valor por defecto: la compilación falla con SYSLIB1051 hasta que añades [MarshalAs]. Ese era el comportamiento de [DllImport].
  • cargo fmt --check --manifest-path … funcionaba en mi máquina (Cargo 1.94) pero fallaba en los runners de CI (Cargo 1.98.1) con Failed to find targets. La CI ahora ejecuta cargo fmt --check desde la carpeta de cada crate.
  • missing_docs = "warn" en [lints] también se aplica a los tests de integración: cada archivo de tests/ es su propio crate y necesita una línea //!.
  • Un doctest de la lección 13 afirmaba que tres esperas concurrentes terminan en menos de 290 ms. Es cierto en mi máquina, pero una aserción de tiempo es un test inestable en runners de CI compartidos, así que el test ahora solo comprueba los resultados.
  • Incluso sin aserción de tiempo, un retardo sigue siendo una carrera: en un runner macOS sobrecargado, un intento de 200 ms «tuvo éxito» a pesar de un timeout de 50 ms (cuando el runtime se despierta después de ambos plazos, timeout sondea primero el future, y este ya está listo). Los intentos lentos del ejercicio 2 duran ahora 5 segundos; el test sigue siendo rápido porque se abandonan tras 50 ms.

Sorpresas:

  • En Rust asíncrono, un .await olvidado significa que el código nunca se ejecuta — lo contrario de C#, donde la tarea arranca de todos modos.
  • El error «future cannot be sent between threads safely» no tiene código E: viene de la restricción Send de tokio::spawn, no del lenguaje.
  • La edición 2024 exige unsafe extern "C" y #[unsafe(no_mangle)]; mucho material FFI antiguo ya no compila tal cual.
  • cargo fmt nunca se había ejecutado en este curso: 30 diferencias de formato en doce lecciones de ejemplos.
  • Hello world en modo release: unos 130 KB en Windows, 430–460 KB en Linux y macOS (runners de CI, Cargo 1.98.1).
  • ¿Cómo se compara rust-analyzer en RustRover con VS Code para estos ejercicios?