I am trying to implement a simple recursive structure in Rust where the type of data that is stored can change with each recursion . The change occurs from any function that is also part of the structure:
// Tipo de funcion que puede recibir Operacion::Cambiar
// que indica que puede cambiar el tipo
type FuncionCambiar<T, U> = fn(T) -> U;
// Estructura recursiva
enum Operacion<T, U, F = FuncionCambiar<T, U>>
where
F: Fn(T) -> U,
{
Mantener(Vec<T>), // Datos fijos, caso base
Cambiar(F, Box<Operacion<T, U>>), // Datos que cambian, recursivo
}
// En cada recursion si hay operaciones "Operacion::Cambiar", el tipo de
// Vec a devolver cambia!
fn eval<T, U>(operacion: &Operacion<T, U, FuncionCambiar<T, U>>) -> Vec<T>
where
std::vec::Vec<T>: std::iter::FromIterator<U>,
{
match operacion {
Operacion::Mantener(vector) => *vector,
Operacion::Cambiar(una_funcion, recursion) => eval(&recursion)
.into_iter()
.map(una_funcion)
.collect::<Vec<T>>(),
}
}
fn main() {
// Defino una funcion que va a cambiar mis datos
let funcion_map = |x| (1, x + 2);
// Defino el enum recursivo con la funcion
let operacion = Operacion::Cambiar(
funcion_map,
Box::new(Operacion::Mantener(vec![1, 2, 3])),
);
let resultado = eval(&operacion);
println!("Resultado -> {:?}", resultado); // Esperaria [(1,3),(1,4),(1,5)]
}
I've finished the Rust book and still can't find a solution to this type of problem. In Haskell I solved it extremely simply and without any drama:
data Operacion a where
Mantener :: [a] -> (Operacion a)
Cambiar :: (a -> b) -> (Operacion a) -> (Operacion b)
eval :: Operacion a -> [a]
eval (Mantener vector) = vector
eval (Cambiar f recursion) = map f (eval recursion)
With this program I can do eval (Cambiar (\x -> (1, x + 2)) (Mantener [1, 2, 3]))
and it effectively shows [(1,3),(1,4),(1,5)]
what I expected.
But I don't know if there will be an equivalent in Rust or I will have to approach the problem with another approach.
Any light on the subject would be greatly appreciated.
As mentioned by a user in my question on the official Rust forum. This language does not have support for Generalized Algebraic Data Types (GADTs) which would be the indicated tool to solve this type of problem.
As an alternative, instead of proposing a scheme where the types belong to the ENUM, making it inflexible, a class approach is proposed, which are allowed to introduce new types: