22 de diciembre de 2020 [Java, Programming, Programming Languages, Rust, Tech]
Algún código Java con errores.
Aquí está mi método principal (en Java). ¿Puedes adivinar el error?
Db db = new Db(); Monitoring monitoring = new Monitoring(); Monitoring mon2 = new Monitoring(); Billing billing = new Billing(db, monitoring); monitoring.setDb(db); runMainLoop(billing, mon2); db.stop(); billing.stop(); monitoring.stop();
Si desea buscar estos 2 errores manualmente, intente leer el código completo aquí: ShutdownOrder.java
¿Pero tal vez ya tengas una idea? ¿Quizás has visto un código como este antes? Si es así, probablemente tenga el instinto de que hay algún tipo de error, incluso si no puede estar seguro de cuál es. ¡Código como este casi siempre tiene errores!
Este código se compila bien, pero contiene dos errores.
Primero, olvidamos setDb() en mon2. Esto provoca una NullPointerException, porque Monitoring siempre espera una base de datos que funcione.
En segundo lugar, y generalmente es más difícil de detectar, cerramos nuestros servicios en el orden incorrecto. Resulta que Monitoring usa su Db cuando está apagado, por lo que obtuvimos una excepción. Peor aún, si es necesario ejecutar algún otro código después de Monitoring.stop(), no se ejecutará porque la excepción nos impide continuar.
Por supuesto, este es un código de juguete, pero problemas como este son comunes (y más difíciles de detectar) en el código de la vida real. De hecho, mi equipo se enfrentó a un error similar esta semana.
Básicamente es difícil conocer sus órdenes de cierre. Se vuelve aún más complicado si la clase también tiene un método start(), que he visto en mucho código Java.
Teniendo en cuenta que esto es simplemente un problema difícil, ¿quizás no valga la pena buscar una herramienta para hacerlo más fácil?
Algo de código Rust sin estos errores
Intentemos escribir este código en Rust. Estos son los métodos principales:
let db = Db::new(); let monitoring = Monitoring::new(&db); let mon2 = Monitoring::new(&db); let billing = Billing::new(&db, &monitoring); run_main_loop(&billing, &mon2); // drop() is called automatically on all objects here
Aquí está el código completo: Shutdown_order.rs
Este código cierra todos los servicios automáticamente al final, y cualquier error que cometamos en esa secuencia son errores de compilación, no cosas que descubrimos más adelante cuando se ejecuta nuestro código.
El código para desactivar cada servicio se ve así:
impl Drop for Monitoring<'_> {
fn drop(&mut self) {
// [Disconnect from monitoring API]
self.db.add_record("MonitorShutDown");
}
}
Esta es una implementación del rasgo Drop para la estructura de Monitoreo (de naturaleza similar a la Interfaz Java). La propiedad Drop es especial: indica qué hacer cuando se elimina una instancia de esta estructura. En Rust, se garantiza que esto sucederá cuando la instancia salga del alcance, razón por la cual nuestros comentarios al final del método principal suenan tan seguros.
Además, el compilador de Rust elimina todo en el orden inverso al que se creó y garantiza que no se utilice nada después de eliminarlo.
El hermoso mundo de Rust nos ofrece dos ventajas relevantes: sin sorpresas y con una vida útil.
Trato número 1: sin ceros inesperados
Primero, en Rust, al igual que en otros lenguajes modernos como Kotlin, debemos describir explícitamente los elementos que pueden faltar. En nuestro ejemplo, podríamos reorganizar el código para que la base de datos nunca se pierda (o sea nula), y el compilador nos anima a hacerlo. Si realmente necesitamos que desaparezca en algún momento, podemos usar el tipo Opción y el compilador nos obligará a manejar el caso cuando desaparezca, en lugar de obtener inesperadamente una NullPointerException como lo hacemos en Java. (De hecho, si estructuramos nuestro código para usar final en tantos lugares como sea posible, básicamente podríamos animarnos a usar la misma solución en Java también).
Trato número 2: de por vida
En segundo lugar, si observa detenidamente el código completo de Shutdown_order.rs, verá muchas anotaciones confusas como <'a> y &’a:
struct Monitoring<'a> { db: &'a Db, }
El significado aproximado de la anotación es: Monitoring tiene una referencia a un Db, y ese Db debería sobrevivir más tiempo que Monitoring.
Las palabras “sobrevivir” son de lo que se trata Rust Lifetimes. La vida es una forma de decir cuánto durará algo.
Las vidas son realmente confusas cuando empiezas con Rust y me han causado mucho dolor. Un código como este es donde más duele y más ayuda. Como mencioné anteriormente, el problema de la orden de cierre es inherentemente difícil. El óxido te produce dolor al principio y, hasta que comprendes lo que está sucediendo, el dolor es muy confuso y agudo. Sin embargo, una vez que su código se compila, es correcto, al menos en lo que respecta a problemas como este.
Me encanta la sensación de seguridad que me da escribir código Rust y saber que el compilador ha verificado mi código para detectar problemas como este, lo que significa que no puede aparecer a las 3 a.m. del día de Navidad…
Nota final/advertencia
Este código de Rust probablemente esté demasiado simplificado, porque todas las referencias son inmutables (no se puede cambiar el objeto al que apuntan). En la práctica, es posible que tengamos referencias mutables y, si las tenemos, tendremos que afrontar más dificultades porque Rust no permitirá que dos objetos diferentes contengan referencias a un objeto si una de esas referencias es mutable. Por lo tanto, habrá objeciones a que Facturación y Monitoreo utilicen objetos Db simultáneamente. Tenemos que hacerlo inmutable (como lo hicimos aquí) o encontrar otra forma de estructurar el código: por ejemplo, podemos almacenar la instancia de Db solo en el código run_main_loop y pasarla temporalmente a los objetos de Facturación y Monitoreo cuando llamamos a sus métodos. Una gran parte del arte, la diversión y la dificultad de aprender Rust es encontrar nuevos patrones para tu código que hagan lo que necesitas hacer y que también hagan feliz al compilador. ¡Cuando lo gestionas, obtienes beneficios increíbles!
PakarPBN
A Private Blog Network (PBN) is a collection of websites that are controlled by a single individual or organization and used primarily to build backlinks to a “money site” in order to influence its ranking in search engines such as Google. The core idea behind a PBN is based on the importance of backlinks in Google’s ranking algorithm. Since Google views backlinks as signals of authority and trust, some website owners attempt to artificially create these signals through a controlled network of sites.
In a typical PBN setup, the owner acquires expired or aged domains that already have existing authority, backlinks, and history. These domains are rebuilt with new content and hosted separately, often using different IP addresses, hosting providers, themes, and ownership details to make them appear unrelated. Within the content published on these sites, links are strategically placed that point to the main website the owner wants to rank higher. By doing this, the owner attempts to pass link equity (also known as “link juice”) from the PBN sites to the target website.
The purpose of a PBN is to give the impression that the target website is naturally earning links from multiple independent sources. If done effectively, this can temporarily improve keyword rankings, increase organic visibility, and drive more traffic from search results.