// Guardarraíl del reciclado de buffers (Bloque 21, palanca 1). // // La invariante: **nadie asigna `last_render = None` a mano**. El único camino // sancionado es `RuntimeState::invalidar_render`, que invalida la caché de // hit-test PERO se queda con el `Vec` en `mount_pool` para que el // mount siguiente escriba encima (a 200k nodos, 432 MB por frame). // // Existe porque el agujero se escapó DOS veces seguidas, y las dos veces fue // invisible: el reciclado compilaba, corría, y no reciclaba nada. La primera vez // porque todo `update` tiraba la caché; la segunda porque quedó un sitio sin // convertir — `dispatch_model`, que se llama con `prim.`, no con `state.`, y por // eso no cayó en el barrido. Es el camino de TODO Msg del canal: el más caliente // que hay. // // Rust no sabe expresar "este campo se lee desde cualquier lado pero se escribe // sólo acá" (no hay visibilidad por campo dentro del mismo módulo), así que la // invariante se sostiene leyendo la fuente. Feo, pero atrapa exactamente lo que // se escapó. use std::fs; use std::path::Path; /// Único uso legítimo: la caché de una ventana **secundaria** /// (`SecRenderCache`), que es otro tipo y no alimenta el pool de la primaria. const PERMITIDO: &str = "sec.last_render = None"; fn recorrer(dir: &Path, hallazgos: &mut Vec) { for entrada in fs::read_dir(dir).expect("leer src/") { let ruta = entrada.expect("entrada").path(); if ruta.is_dir() { recorrer(&ruta, hallazgos); } else if ruta.extension().is_some_and(|e| e == "rs") { let fuente = fs::read_to_string(&ruta).expect("leer fuente"); for (i, linea) in fuente.lines().enumerate() { let l = linea.trim(); // Sólo asignaciones reales: descarta comentarios y doc. if l.starts_with("//") { continue; } if l.contains("last_render = None") && !l.contains(PERMITIDO) { hallazgos.push(format!("{}:{}: {l}", ruta.display(), i + 1)); } } } } } #[test] fn nadie_tira_el_mounted_a_mano() { let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); let mut hallazgos = Vec::new(); recorrer(&src, &mut hallazgos); assert!( hallazgos.is_empty(), "asignación cruda de `last_render = None` — usá `invalidar_render()`, que \ conserva los buffers del árbol montado en `mount_pool`:\n {}", hallazgos.join("\n ") ); }