简体   繁体   中英

Rust: Trait RangeBounds cannot be made into an object

I sumbled upon a problem while trying out Rust, that might point to a bigger non understanding of its concepts on my part.

My goal is to programm a variant of Conway's Game of Life. I want the values when cells are created or stay alive not to be hard coded, but within a struct. My first attempt was to create a struct

use std::ops::Range;

struct Rules {
    be_born: Range<usize>,
    stay_alive: Range<usize>,
}

impl Rules {
  pub fn new(be_born: Range<usize>, stay_alive: Range<usize>) -> Rules {
    Rules { be_born, stay_alive }
  }
}

let rules = Rules::new(2..4, 3..6);

This object is later used within the algorithm that iterates over all the cells. It works fine, until I also want to allow other kind of Ranges during creation like for example RangeTo (2..=3).

I know I could rewrite the struct Rules to be generic.

use std::ops::RangeBounds;

struct Rules<BR: RangeBounds<usize>, AR: RangeBounds<usize>> {
    be_born: BR,
    stay_alive: AR,
}

This in turn would force me to make all the algorithms I use to be generic as well. This seems to be quite a lot of overhead just to include two simple ranges.

On the other hand none of my attempts to include variables of type RangeBounds directly into my struct succeeded. I tried &dyn RangeBounds<usize> or Box<&dyn RangeBounds<usize>> , just to always get the error E0038 that I cannot make this trait into an object.

Is there any other way to get this done, or is there some other feasable way I do not see?

Thank you in advance for all your hints.

For a broad answer, we need to know how exacly you are iterating over be_born and stay_alive fields. But for solving the problem you point out that you want to use different kind of ranges, the easiest way is specifying that be_born and stay_alive fields are both an Iterable that returns usize when iterated over, aka Iterator<Item=usize> :

struct Rules<T, U> 
where
    T: Iterator<Item=usize>,
    U: Iterator<Item=usize>
{
    be_born: T,
    stay_alive: U,
}

impl<T, U> Rules<T, U>
where
    T: Iterator<Item=usize>,
    U: Iterator<Item=usize>
{
    pub fn new(be_born: T, stay_alive: U) -> Self {
        Self { be_born, stay_alive }
    }
}

fn main() {
    let rule = Rules::new(2..4, 3..6);
    let other_rule = Rules::new(2..=4, 3..=6);
    let another_rule = Rules::new(2..=4, 3..6);

    for x in rule.be_born {
        println!("born: {}", x);
    }
    for y in rule.stay_alive {
        println!("alive: {}", y);
    }
}

This will also allow you to assign non-range but iterable types to be_born and stay_alive fields. If you want to restrict yourself to range types only, you can replace every Iterator<Item=usize> in the code with Iterator<Item=usize> + RangeBounds<usize> , which means "a type which both implements Iterator<Item=usize> and RangeBounds<usize> ".

For further usage of Iterator trait, see the book .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM