hakunin
hakunin

Reputation: 4231

Construct vector of of types and initialize them

I am trying to have some sort of list of types which I can then initialize and build genetic algorithms.

The issue is working with "types" themselves, I haven't found the right way to take an array of types and call ::new on each to get an "instance".

// --- conditions.rs ----------------------------------
extern crate strum;
use strum_macros::{EnumIter}; // etc.

pub trait Testable {
    fn new() -> Self;
    fn test(&self, candle: &Candle) -> bool;
}

// use strum::IntoEnumIterator;
#[derive(EnumIter,Debug)]
pub enum Conditions {
    SmaCheck,
    CandlesPassed,
}

// simple moving average check
pub struct SmaCheck {
    sma1: i8,
    sma2: i8,
}

impl Testable for SmaCheck {
    fn new() -> Self {
        Self { sma1: 10, sma2: 20 }
    }
    fn test(&self, candle: &Candle) -> bool {
        return true;
    }
}


// --- generator.rs -----------------------------------
// creates random conditions, which will then be mutated and bred

use strum::IntoEnumIterator;
use crate::conditions::{Conditions, Testable};


pub fn run() {
    for condition in Conditions::iter() {
        println!("{:?}", condition); // this works

        let testable = condition::new(); // undeclared type or module `condition`
        println!("{:?}", testable::new());
    }
}

Upvotes: 1

Views: 273

Answers (1)

SOFe
SOFe

Reputation: 8214

It seems that you have an enum that represents types, and a struct with the same name as an enum variant.

Note that enum variant names have nothing to do with any types they might represent.

In addition, reflections do not exist (not in the Java sense) in Rust, so you can't have a value that contains a type name and create that type from it.

However, it is possible to take an enum of unknown value (the type checker cannot constraint the enum values anyway), and return a value based on this enum.

Furthermore, while a method may not return unknown types directly, you can use a Box<dyn Trait> to wrap a value of unknown type, or create an enum that implements the trait and delegates to sealed implementations.

The following might be closer to what you wanted:

pub enum ConditionTypes {
    SmaCheck,
    CandlesPassed,
}

pub enum Condition {
    SmaCheck(SmaCheck), // the first word is the enum variant, the second word is the type of the value it contains
    CandlesPassed(CandlesPassed),
}

impl ConditionType {
    pub fn new(&self) -> Condition {
        match self {
            Self::SmaCheck => Condition::SmaCheck(SmaCheck::new()),
            Self::CandlesPassed => Condition::CandlesPassed(CandlesPassed::new()),
        }
    }
}

pub trait Testable {
    // we don't really need new() in this trait, do we?
    // if you want to use this trait when it could be of runtime-unknown type
    // instead of compile-time generics, all methods must have a receiver.

    fn test(&self, candle: &Candle) -> bool;
}

impl Testable for Condition {
    fn test(&self, candle: &Candle) -> bool {
        match self {
            Condition::SmaCheck(inner) => inner.test(candle),
            Condition::CandlesPassed(inner) => inner.test(candle),
        }
    }
}

// impl Testable for SmaCheck and CandlesPassed omitted

This looks a bit boilerplate, but there are macros to derive this.

Upvotes: 1

Related Questions