generics rust traits

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> esperado Rustmark<P, R> , Rustmark<parser::DefaultParser, renderers::html::HTMLRenderer> encontrado Rustmark<parser::DefaultParser, renderers::html::HTMLRenderer> (parámetro de tipo esperado, encontrado struct parser::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.