04 de marzo de 2020 [Programming, Programming Languages, Rust, Tech]
Me encanta escribir código en Rust. Muchas cosas sobre el idioma y su ecosistema se sienten así. Bien*.
* Por ejemplo: propiedad de objetos, sistema de tipos expresivos, compilación en documentos API nativos y sin conexión, inmutabilidad, bibliotecas de alta calidad.
Una de las cosas que me gusta es que no siento la necesidad de usar un IDE, por lo que puedo codificar felizmente en Vim sin ningún complemento inteligente.
Una cosa que el IDE podría ofrecerme es una refactorización de la “función de extracción”. En la mayoría de los idiomas, estoy feliz de hacerlo manualmente, porque puedo dejar que los errores de compilación me guíen sobre cómo debería verse mi función.
Sin embargo, en Rust a veces tengo problemas para encontrar la firma correcta para la función que quiero extraer y tengo problemas para convencer al compilador de que me ayude.
Aquí hay un ejemplo de mi nuevo proyecto listsync, en listsync-client-rust.rs:
use actix_web::middleware, App, HttpServer;
use listsync_client_rust;
// ...
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
//...
HttpServer::new(||
App::new()
.wrap(listsync_client_rust::cookie_session::new_session())
.wrap(middleware::Logger::default())
.configure(listsync_client_rust::config)
)
//...
Quiero extraer el código resaltado arriba, la creación de la aplicación, en una función separada, como esta:
fn new_app() -> ??? App::new() .wrap(listsync_client_rust::cookie_session::new_session()) .wrap(middleware::Logger::default()) .configure(listsync_client_rust::config) //... HttpServer::new(|| new_app() )
Sencillo, ¿verdad? Para saber cuál debería ser el tipo de retorno de una función, sólo puedo hacer malas conjeturas y hacer que el compilador me diga qué estoy haciendo mal. En este caso, lo adivinaré cambiando el signo de interrogación de arriba a i32 y ejecutando una prueba de carga. Recibo varios errores, uno de los cuales es:
error[E0277]: the trait bound `i32: actix_service::IntoServiceFactory<_>` is not satisfied
--> src/bin/listsync-client-rust.rs:27:5
|
27 | / HttpServer::new(|| )
| |______^ the trait `actix_service::IntoServiceFactory<_>` is not implemented for `i32`
|
= note: required by `actix_web::server::HttpServer`
Entonces, el primer problema que veo es que el mensaje de error que veo es sobre el siguiente código y no hay ningún error sobre mi nueva función.
Claramente me estaba moviendo demasiado rápido. Cambiemos el código HttpServer::new a como estaba y simplemente creemos una nueva función new_app. Ahora me sale un error que debería ayudarme:
error[E0308]: mismatched types --> src/bin/listsync-client-rust.rs:12:5 | 11 | fn new_app() -> i32 { | --- expected `i32` because of return type 12 | / App::new() 13 | | .wrap(listsync_client_rust::cookie_session::new_session()) 14 | | .wrap(middleware::Logger::default()) 15 | | .configure(listsync_client_rust::config) | |________________________________________________^ expected i32, found struct `actix_web::app::App` | = note: expected type `i32` found type `actix_web::app::App<impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog<actix_http::body::Body>>`
¡Entonces el compilador nos ha dicho qué tipo devolvemos! Copiemos eso en una firma de tipo de función:
use actix_service::ServiceFactory;
use actix_http::body::Body;
// ...
fn new_app() -> App<impl ServiceFactory, middleware::logger::StreamLog<Body>> {
// ...
El primer error que recibí del compilador fue un problema técnico:
error[E0432]: unresolved import `actix_service`
--> src/bin/listsync-client-rust.rs:1:5
|
1 | use actix_service::ServiceFactory;
| ^^^^^^^^^^^^^ use of undeclared type or module `actix_service`
Pude solucionar este problema agregando actix-service=”1.0.5″ a Cargo.toml. (Encontré una versión buscando en Cargo.lock, porque esta dependencia ya se usa implícitamente; solo necesito hacerla explícita si voy a usarla directamente).
Una vez que hice eso, recibí el siguiente error:
error[E0603]: module `logger` is private
--> src/bin/listsync-client-rust.rs:13:54
|
13 | fn new_app() -> App<impl ServiceFactory, middleware::logger::StreamLog<Body>> {
| ^^^^^^
Esto me deja un poco estancado: no puedo usar StreamLog porque está en un módulo privado.
Más importante aún, esto muestra que en realidad no quiero ser tan específico como lo hice: no me importa cuál sea el parámetro de tipo real para la Aplicación; solo quiero devolver una Aplicación y que el compilador complete los espacios en blanco. Idealmente, si cambio el contenido de new_app más adelante, por ejemplo para agregar otra llamada contenedora que cambie el tipo de aplicación que devolvemos, me gustaría dejar el tipo de devolución igual y que funcione.
Con eso en mente, miré el tipo HttpServer::nuevos usos. Aquí está la impl. de HttpServer:
impl<F, I, S, B> HttpServer<F, I, S, B> where
F: Fn() -> I + Send + Clone + 'static,
I: IntoServiceFactory<S>,
S: ServiceFactory<Config = AppConfig, Request = Request>,
S::Error: Into<Error> + 'static,
S::InitError: Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
y HttpServer::new se parece a:
pub fn new(factory: F) -> Self
Entonces necesitamos una función que realmente cree la Aplicación, y el tipo de esa función es F, es decir, Fn que devuelve I + Enviar + Clonar + ‘estático. De la declaración implícita de HttpServer podemos ver que el tipo I depende de S y B, que tienen tipos bastante complejos. Metámoslo todo en:
use actix_http::Error, Request, Response;
use actix_service::IntoServiceFactory;
use actix_web::body::MessageBody;
use actix_web::dev::AppConfig, Service;
use core::fmt::Debug;
// ...
fn new_app<I, S, B>() -> I
where
I: IntoServiceFactory<S> + Send + Clone + 'static,
S: ServiceFactory<Config = AppConfig, Request = Request>,
S::Error: Into<Error> + 'static,
S::InitError: Debug,
S::Response: Into<Response<B>> + 'static,
<S::Service as Service>::Future: 'static,
B: MessageBody + 'static,
App::new()
.wrap(listsync_client_rust::cookie_session::new_session())
.wrap(middleware::Logger::default())
.configure(listsync_client_rust::config)
Tenga en cuenta que tuve que modificar I para incluir requisitos adicionales en el tipo de devolución F de la definición de HttpServer. (Creo que estoy haciendo lo correcto, pero no estoy seguro. Si elimino +Send+Clone+’static parece que el comportamiento es el mismo).
Ahora recibo este error del compilador:
error[E0308]: mismatched types
--> src/bin/listsync-client-rust.rs:27:5
|
17 | fn new_app<I, S, B>() -> I
| - expected `I` because of return type
...
27 | / App::new()
28 | | .wrap(listsync_client_rust::cookie_session::new_session())
29 | | .wrap(middleware::Logger::default())
30 | | .configure(listsync_client_rust::config)
| |________________________________________________^ expected type parameter, found struct `actix_web::app::App`
|
= note: expected type `I`
found type `actix_web::app::App<impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog<actix_http::body::Body>>`
= help: type parameters must be constrained to match other types
= note: for more information, visit
El compilador realmente intentó ayudar aquí, sugiriéndome que leyera un capítulo del Libro Rust, pero incluso después de leerlo no tenía idea de cómo hacer lo que estaba tratando de hacer.
¿Alguien puede ayudarme?
¿No sería fantástico si hubiera una forma en que el compilador pudiera brindarme una ayuda más comprensible para resolver este problema?
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.