Как реализовать IntoIterator на StackVec - PullRequest
0 голосов
/ 12 октября 2019

Я пытаюсь написать Vec подобный объект с фиксированным размером для использования в стеке.

Это часть онлайн-курса, который я пытаюсь выполнить Задание 1, фаза2A .

У меня проблемы с реализацией черты IntoIterator.

Код для StructVec

#![no_std]

pub struct StackVec<'a, T: 'a> {
    storage: &'a mut [T],
    len: usize
}

impl<'a, T: 'a> StackVec<'a, T> {
    pub fn new(storage: &'a mut [T]) -> StackVec<'a, T> {
        StackVec { storage, len: 0 }
    }

    pub fn with_len(storage: &'a mut [T], len: usize) -> StackVec<'a, T> {
        StackVec { storage, len }
    }

    pub fn capacity(&self) -> usize {
        self.storage.len()
    }

    pub fn truncate(&mut self, len: usize) {
        if len < self.len {
            self.len = len;
        }
    }

    pub fn into_slice(self) -> &'a mut [T] {
        &mut self.storage[..self.len]
    }

    pub fn as_slice(&self) -> &[T] {
        &self.storage[..self.len]
    }

    pub fn as_mut_slice(&mut self) -> &mut [T] {
        &mut self.storage[..self.len]
    }

    pub fn len(&self) -> usize {
        self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn is_full(&self) -> bool {
        self.len == self.storage.len()
    }

    pub fn push(&mut self, value: T) -> Result<(), ()> {
        if self.is_full() {
            return Err(());
        }
        self.storage[self.len] = value;
        self.len += 1;
        Ok(())
    }
}

Мне удалось получить инструментчерта IntoIterator, возвращающая итератор базового массива:

impl<'a, T: 'a> IntoIterator for StackVec<'a, T> {
    type Item = &'a mut T;
    type IntoIter = core::slice::IterMut<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.storage.into_iter()
    }
}

Однако это не совсем то, чего я хочу, поскольку он будет перебирать весь массив, а не только элементы, которые были помещены в него.

Я пытался вернуть итератор из среза массива базового массива, но меня ловит время жизни:

impl<'a, T: 'a> IntoIterator for StackVec<'a, T> {
    type Item = &'a T;
    type IntoIter = core::slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.as_slice().into_iter()
    }
}

Не удается скомпилировать с ошибкой

error[E0597]: `self` does not live long enough
   --> src/lib.rs:165:9
    |
165 |         self.as_slice().into_iter()
    |         ^^^^ borrowed value does not live long enough
166 |     }
    |     - borrowed value only lives until here
    |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 160:1...
   --> src/lib.rs:160:1
    |
160 | / impl<'a, T: 'a> IntoIterator for StackVec<'a, T> {
161 | |     type Item = &'a T;
162 | |     type IntoIter = core::slice::Iter<'a, T>;
163 | |
...   |
166 | |     }
167 | | }
    | |_^

Я также пытался создать новый Iterator полностью, но меня снова поймали!

struct Iter<'a, T: 'a> {
    stack_vec: StackVec<'a, T>,
    start: usize,
}

impl<'a, T: 'a> Iterator for Iter<'a, T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        let start = self.start;
        if start < self.stack_vec.len {
            self.start += 1;
            Some(self.stack_vec[start])
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let size = self.stack_vec.len - self.start;
        (size, Some(size))
    }
}

Не удается скомпилировать с ошибкой:

error[E0508]: cannot move out of type `[T]`, a non-copy slice
   --> src/lib.rs:148:18
    |
148 |             Some(self.stack_vec[start])
    |                  ^^^^^^^^^^^^^^^^^^^^^ cannot move out of here

Я пыталсяглядя на ящики, которые делают то же самое, но либо используют библиотеку std, котораяЯ не могу использовать это назначение, или я не понимаю, как перенести их код на мой.

https://github.com/bluss/arrayvec

https://github.com/danielhenrymantilla/stackvec-rs/blob/master/src/stackvec/traits/into_iter.rs

Что яделать неправильно?

1 Ответ

1 голос
/ 13 октября 2019

Как довольно простое расширение вашей первоначальной идеи, вы можете ограничить количество элементов, которые может возвращать итератор, используя метод take. Это превращает вашу IntoIterator реализацию в:


impl<'a, T: 'a> IntoIterator for StackVec<'a, T> {
    type Item = &'a mut T;
    type IntoIter = std::iter::Take<core::slice::IterMut<'a, T>>;

    fn into_iter(self) -> Self::IntoIter {
        self.storage.into_iter().take(self.len)
    }
}

Вы столкнулись с проблемами при другой попытке реализовать IntoIterator, поскольку метод into_iter становится владельцем self. Это означает, что после выхода из функции вам необходимо убедиться, что вы переместили или скопировали все то, что хотите использовать в своем итераторе, из self. Поскольку as_slice только заимствует, вы не можете вернуть итератор в срез из своей функции (данные, от которых он заимствует, будут удалены).

impl<'a, T: 'a> IntoIterator for StackVec<'a, T> {
    type Item = &'a T;
    type IntoIter = core::slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        //       ^ no & or &mut here means this function takes ownership of self
        self.as_slice().into_iter()
    }
}

Вы все еще можете создать не владеющий итераторваша StackVec структура, но вы не сделаете это с IntoIterator. Вместо этого просто создайте метод в своей структуре с именем iter:

pub fn iter(&self) -> impl Iterator<Item=&T> {
    self.storage.iter().take(self.len)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...