Estou escrevendo um Rust crate onde tenho uma struct cujo campo implementa uma característica, e gostaria de fornecer um recurso onde:
#[cfg(not(feature = "dyn"))]
: o campo struct éT
despachado estaticamente (mais eficiente, mas menos flexível em tempo de execução)#[cfg(feature = "dyn")]
: struct field éBox<dyn MyTrait>
um objeto de característica despachado dinamicamente (flexibilidade de tempo de execução com pequeno custo de desempenho)
trait MyTrait {}
#[cfg(not(feature = "dyn"))]
struct MyStruct<T: MyTrait> {
my_field: T
}
#[cfg(feature = "dyn")]
struct MyStruct {
my_field: Box<dyn MyTrait>
}
Meu problema é que cada impl
bloco precisa ser duplicado por completo, quando as alterações deveriam afetar apenas algumas linhas.
#[cfg(not(feature = "dyn"))]
impl<T: MyTrait> MyStruct<T> {
fn new(field: T) -> Self {
Self { field }
}
}
#[cfg(feature = "dyn")]
impl MyStruct {
fn new(field: impl MyTrait + 'static) -> Self {
Self { field: Box::new(field) }
}
}
Existe uma maneira mais elegante de fazer isso? Macro black magic? Isso é apenas uma má ideia e não vale a pena fazer?
Editar
Por resposta de @cdhowie
Isso quase chega à compilação bem-sucedida! No entanto, MyTrait
na verdade tem um método (que eu retirei para obter um exemplo mínimo):
trait MyTrait: Sized {
fn calculate(&self, struct: &MyStruct<Self>);
}
a referência &MyStruct<Self>
é necessária porque esse método precisa acessar dados para cálculos.
Tentar implementar como sugerido não funciona... algo sobre isso quebra a compatibilidade dinâmica
impl MyTrait for Box<dyn MyTrait> {
// ^^^^^^^^^^^^^^^^
// ERROR
// the trait `MyTrait` cannot be made into an object
Se eu tentar implementar a característica para Box (não tenho certeza se isso resolveria meu problema), esse é o problema:
impl<T: MyTrait> MyTrait for Box<T> {
fn calculate(
&self,
struct: &MyStruct<Self>,
) {
(**self).calculate(struct)
// ^^^^^^
// ERROR: mismatched types
// expected reference &MyStruct<T>
// found reference &MyStruct<Box<T>>
}
}