Реализация абстракций, по канону как?

Вот допустим у меня есть примерно такой Java-код:

public enum SegmentType {
    CLOSE, MOVE_TO, LINE_TO, SPLINE_TO
}

public interface Segment {
	
    SegmentType getType();

    Object[] getParams();
}

public final class MoveSegment implements Segment{
    private Integer[] point = new Integer[2];

    public MoveSegment( int x, int y ){
        point[0] = x;
        point[1] = y;
    }

    public SegmentType getType(){
        return MOVETO;
    }

    public Object[] getParams(){
        return point;
    }
}

public final class CloseSegment implements Segment{

    public SegmentType getType(){
        return CLOSE;
    }

    public Object[] getParams(){
        return null;
    }
}

public interface Shape extends Iterable<Shape>{
}

void travel( Shape shape ){
	for( Segment segment: shape ){
		switch( segment.getType() ){
			case MOVETO: break;
			case CLOSE: break;
		}
	}
}

Как подобное лучше реализовать на Rust’е?
Если с Segment’ом вполне понятно:

pub enum Segment<T> {
    Close,
    MoveTo( T, T ),
}

То как по хорошему должен выглядить Shape?
ПОнятно, что самый простой способ:

pub trait Shape<T> {
    fn get_path(&self) -> Vec<Segment<T>>;
}

Но не будет ли там слишком много копирования данных?
Хотелось бы итерировать по ссылкам.

Набросал небольшой пример, возможно он вам поможет. Если от итерирования требуется какая-то логика, например, прекращать итерирование при Close - придется реализовать Iterator для Shape.
https://is.gd/G9pAX0

Что же касается копирования данных - не занимайтесь преждевременной оптимизацией. enum такого же размер, как самый большой его элемент, не думаю, что стоит экономить на копировании 16 байт.

1 лайк

Чтобы избежать копирования можно реализовать IntoIterator для ссылок на структуры Shape-ов. Как-то так:

#[derive(Debug)]
struct Pos<T>(T, T);

#[derive(Debug)]
struct Line<T>{
    p0: Pos<T>,
    p1: Pos<T>
}

#[derive(Debug)]
struct Poly<T>(Vec<Pos<T>>);


struct LineIter<'l, T:'l>{
    line:&'l Line<T>,
    pn:usize
}

impl<'l, T> Iterator for LineIter<'l, T> {
    type Item = &'l Pos<T>;
    fn next(&mut self) -> Option<Self::Item>{
        match self.pn {
            0 => {self.pn += 1; Some(&self.line.p0)}
            1 => {self.pn += 1; Some(&self.line.p1)}
            _ => None
        }
    }
}

struct PolyIter<'p, T:'p>{
    poly:&'p Poly<T>,    
    pn: usize,
}


impl<'p, T> Iterator for PolyIter<'p, T> {
    type Item = &'p Pos<T>;
    fn next(&mut self) -> Option<Self::Item>{
        if self.pn < self.poly.0.len() {
            let i = self.pn;
            self.pn += 1;
            Some(&self.poly.0[i])
        }else{
            None
        }
    }
}

impl<'l, T> IntoIterator for &'l Line<T> {
    type Item = &'l Pos<T>;
    type IntoIter = LineIter<'l, T>;

    fn into_iter(self) -> Self::IntoIter {
        LineIter{
            line:self,
            pn:0
        }
    }
}

impl<'p, T> IntoIterator for &'p Poly<T> {
    type Item = &'p Pos<T>;
    type IntoIter = PolyIter<'p, T>;

    fn into_iter(self) -> Self::IntoIter {
        PolyIter{
            poly:self,
            pn:0
        }
    }
}


fn main() {
    let line = Line{p0:Pos(1,2), p1:Pos(3,4)};
    let poly = Poly(vec![Pos(1,2), Pos(3,4), Pos(5,6), Pos(7,8)]);
    
    //iterate by ref
    for p in &line { println!("line pos = {:?}", p)};
    for p in &poly { println!("poly pos = {:?}", p)};
    
    //line and poly does not moved
    println!("{:?}", line);
    println!("{:?}", poly);
}

Если нужен свой типаж, функция которого возвращает итератор, то без использования Box или impl Trait (только nightly) можно сделать так:

trait ShapeIter<'i, T:'i, I: Iterator<Item = &'i Pos<T>>> {
    fn iter(&'i self) -> I;
}

impl<'i, T> ShapeIter <'i, T, LineIter<'i, T>> for Line<T> {
    fn iter(&'i self) -> LineIter<'i, T>{
        LineIter{
            line:self,
            pn:0
        }        
    }
} 

impl<'i, T> ShapeIter <'i, T, PolyIter<'i, T>> for Poly<T> {
    fn iter(&'i self) -> PolyIter<'i, T>{
        PolyIter{
            poly:self,
            pn:0
        }        
    }
} 
2 лайка