大小问题 动态分派 迭代器 rust

3
我正在实现一个结构体,它持有对状态集合的引用,并能够以循环方式遍历该引用。
struct Pawn {
    _state: Box<dyn Iterator<Item = u8>>,
}

impl Pawn {

    const ALL_STATES: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    fn new() -> Self {
        Pawn { _state: Box::new(Self::ALL_STATES.into_iter().cycle()) }
    }

    fn tick(&mut self, steps: usize) -> u8 {
        (0..steps - 1).for_each(|_| {self._state.next();});
        self._state.next().unwrap()
    }
}

impl Clone for Pawn {

    fn clone(&self) -> Self {
        Self { _state: Box::new(*self._state.as_ref().clone()) }
    }
}

жһ„йҖ еҮҪж•°е’Ң`tick`ж–№жі•йғҪжҢүз…§йў„жңҹе·ҘдҪңгҖӮдҪҶжҳҜжҲ‘д№ҹжғідёәиҝҷдёӘз»“жһ„дҪ“е®һзҺ°`Clone`гҖӮиҝҷе°ұжҳҜжҲ‘иҝ·еӨұзҡ„ең°ж–№пјҡ
the size for values of type `dyn Iterator<Item = u8>` cannot be known at compilation time
the trait `Sized` is not implemented for `dyn Iterator<Item = u8>`

似乎我无法在编译时不知道的情况下创建一个新的Box,这是由于动态分派造成的。我知道它将始终指向一个u8的迭代器,但我不知道如何告诉编译器。

如果您知道它将始终指向一个u8的迭代器,为什么_state不是一个Iterator<Item = u8>呢? - mkrieger1
如果你知道它将始终指向一个u8的迭代器,为什么_state不是一个Iterator<Item = u8>呢? - mkrieger1
如果你知道它将始终指向一个u8的迭代器,为什么_state不是一个Iterator<Item = u8>呢? - undefined
移除盒子并不能解决问题。现在整个Pawn结构对编译器来说是“大小未知”的。盒子使得Pawn结构本身具有大小。如果您有不使用盒子的建议,也欢迎提出。 - hasdrubal
移除盒子并不能解决问题。现在整个Pawn结构对编译器来说是“大小未知”的。盒子使得Pawn结构本身具有大小。如果你有一个不使用盒子的建议,也欢迎提出。 - hasdrubal
2个回答

3
既然你知道_state的实际类型是Sized,你可以摒弃所有的trait对象,直接定义它。

struct Pawn {
    _state: std::iter::Cycle<std::array::IntoIter<u8, 10>>
}

impl Pawn {
    fn new() -> Self {
        // as before, but remove Box::new
    }
}

impl Clone for Pawn {

    fn clone(&self) -> Self {
        Self { _state: self._state.clone() }
    }
}

请注意,如果您不知道确切的_state类型,只知道它是一个产生u8的迭代器,您仍然可以在Pawn中定义克隆函数,只需将其类型参数化即可。考虑以下内容:
struct Pawn<S>
where
    S: Iterator<Item = u8>,
{
    _state: S,
}

impl Pawn<std::iter::Cycle<std::array::IntoIter<u8, 10>>> {
    const ALL_STATES: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    fn new() -> Self {
        Pawn {
            _state: Self::ALL_STATES.into_iter().cycle(),
        }
    }

    fn tick(&mut self, steps: usize) -> u8 {
        (0..steps - 1).for_each(|_| {
            self._state.next();
        });
        self._state.next().unwrap()
    }
}

// Note the extra trait bound here! S must also be clone
impl<S> Clone for Pawn<S>
where
    S: Iterator<Item = u8> + Clone,
{
    fn clone(&self) -> Self {
        Self {
            _state: self._state.clone(),
        }
    }
}

2
也许你在这个问题中简化了很多,但是如果大致上是你的使用情况,我觉得这有点过度设计。也许你可以考虑一个更简单的设计,比如:
#[derive(Clone, Copy)]
struct SimplePawn {
    state: u8,
}

impl SimplePawn {
    const STATES: usize = 10;

    fn new() -> Self {
        Self {
            state: 1,
        }
    }

    fn tick(&mut self, steps: usize) -> u8 {
        let mut state = self.state as usize - 1;
        state = (state + steps - 1) % Self::STATES + 1;
        let state = state as u8;
        self.state = state + 1; 
        state
    }
}

#[test]
fn test_equivalent() {
    // Where `Pawn` is the type that @AdamSmith suggested
    let mut pawn = Pawn::new();
    let mut simple_pawn = SimplePawn::new();
    
    for ticks in 1..1000 {
        assert_eq!(pawn.tick(ticks), simple_pawn.tick(ticks));
    }
}

1
我认为这是针对这个特定问题的更好答案,但对于一般的循环状态,上述设计是合适的。你得到了我的赞同 :p - hasdrubal
1
我认为这是针对这个特定问题的更好答案,但对于一般的循环状态,上述设计是合适的。你得到了我的赞同 :p - hasdrubal
没问题,这很公平 :) - isaactfa
没问题,这样挺公平的 :) - isaactfa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接