generics - Error genérico: parámetro de tipo esperado, estructura encontrada
rust traits (2)
Comencé un nuevo proyecto, donde quiero ser lo más modular posible, con eso quiero decir que me gustaría poder reemplazar algunas partes con otras en el futuro.
Este parece ser un uso perfecto para los
traits
, en este momento tengo este código:
mod parser;
mod renderer;
mod renderers;
use parser::MarkParser;
use renderer::MarkRenderer;
struct Rustmark <P: MarkParser, R: MarkRenderer> {
parser: P,
renderer: R,
}
impl <P: MarkParser, R: MarkRenderer> Rustmark <P, R> {
fn new() -> Rustmark <P, R> {
Rustmark {
parser: parser::DefaultParser::new(),
renderer: renderers::HTMLRenderer::new(),
}
}
fn render(&self, input: &str) -> &str {
self.renderer.render(self.parser.parse(input))
}
}
Pero recibo un par de errores, principalmente este:
error: tipos no coincidentes:
Rustmark<P, R>
esperadoRustmark<P, R>
,Rustmark<parser::DefaultParser, renderers::html::HTMLRenderer>
encontradoRustmark<parser::DefaultParser, renderers::html::HTMLRenderer>
(parámetro de tipo esperado, encontrado structparser::DefaultParser
) [E0308]
Y un par de errores de por vida como este:
error: no se puede inferir una vida útil adecuada para la coerción automática debido a requisitos en conflicto
ayuda: considere usar un parámetro de duración explícita como se muestra:
fn parse<''a>(&''a self, input: &''a str) -> &str
He intentado varios ajustes para que funcione, pero ninguno de ellos pareció apaciguar al compilador. Así que quería saber si este es el enfoque correcto y qué podría hacer para que funcione.
Primer error: crea un objeto
Rustmark
con el
parser
de campo de tipo
DefaultParser
y el
renderer
de campo de tipo
HTMLRenderer
, pero se espera que la función devuelva
Rustmark <P, R>
.
En general, P no es del tipo
DefaultParser
y R no es del tipo
HTMLRenderer
, por lo que nunca se compilará.
Una buena solución si desea tener valores predeterminados del tipo correcto es vincular
P
y
R
para implementar el
trait
Default
, de esta manera:
use std::default:Default;
impl <P: MarkParser + Default, R: MarkRenderer + Default> Rustmark <P, R> {
fn new() -> Rustmark <P, R> {
Rustmark {
parser: P::default(),
renderer: R:default(),
}
}
}
Segundo error: ese problema principal es que devuelve una referencia de algo que probablemente morirá dentro del método de
render
(la
String
que asigna en el método de
render
interna probablemente).
El compilador le dice que no conoce la vida útil del objeto que señala esa referencia, por lo que no puede garantizar que la referencia sea válida.
Puede especificar un parámetro de por vida, pero en su caso, probablemente la mejor solución es devolver el objeto
String
sí, no una referencia.
Siguiendo la respuesta de
Andrea P
, busqué el rasgo
default
en
default
.
Que se define así:
pub trait Default {
fn default() -> Self;
}
Y lo que terminé haciendo no fue usar el rasgo
default
sino agregar un
constructor
new
en mis rasgos
MarkParser
y
MarkRenderer
como este:
pub trait MarkParser {
fn new() -> Self;
fn parse(&self, input: &str) -> &str;
}
La pieza clave que no conocía era la palabra clave
Self
y de esta manera puedo escribir mi implementación de esta manera:
impl <P: MarkParser, R: MarkRenderer> Rustmark <P, R> {
fn new() -> Rustmark <P, R> {
Rustmark {
parser: P::new(),
renderer: R::new(),
}
}
fn render(&self, input: &str) -> &str {
self.renderer.render(self.parser.parse(input))
}
}
Esto es exactamente lo mismo que implementar el rasgo
Default
, excepto que puedo usar
new
lugar del
default
que prefiero
y
no tengo que agregar el rasgo
Default
a la implicación de RustMark.
impl <P: MarkParser, R: MarkRenderer> Rustmark <P, R> {
en vez de
impl <P: MarkParser + Default, R: MarkRenderer + Default> Rustmark <P, R> {
Muchas gracias a Andrea P por señalarme en la dirección correcta.