
www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1705r0.html
Size: a a a
Read
. Подразумевается, что тип, реализующий BufRead
, имеет некоторый внутренний буфер, доступ к которому можно получить при помощи fill_buf. Наличие этого внутреннего буфера позволяет реализовать некоторые операции, которые затруднительно реализовывать непосредственно поверх Read
, например, read_until.BufRead
— это lines, который возвращает итератор строк. Но что именно возвращает этот итератор? Он возвращает Result<String, std::io::Error>
. То есть каждый вызов Iterator::next
выделяет память в куче под строку. Как-то не zero cost. В конце концов, у типа, реализующего BufRead
, есть некий внутренний буфер, так почему нельзя возвращать ссылку на него? Давайте посмотрим внимательнее на типы.Iterator::next
имеет следующую сигнатуру:fn next(&mut self) -> Option<Self::Item>;Обратите внимание: несмотря на то, что в
next
неявно входит лайфтайм, этот лайфтайм отсутствует в возвращаемом типе! Это означает, что значение, возвращаемое next
, может быть, и не владеющее, но оно по крайне мере точно ничего не заимствует из self
. А как должен выглядеть гипотетический next
у нашего итератора, который возвращает ссылку на внутренний буфер?fn next<'a>(&'a mut self) -> Option<&'a str>;(Я сейчас намеренно опускаю вопрос об ошибках, потому что это не главное в том, что я хочу донести). Чем эта сигнатура кардинально отличается от
Iterator::next
? А тем, что (я даже специально расписал лайфтаймы, чтобы это было видно более ясно) возвращаемое значение заимствует self
! Таким образом, даже не смотря на то, что технически мы можем написать этот метод, он не подходит под сигнатуру Iterator::next
, а значит, мы не можем использовать все эти полезные итераторные комбинаторы.trait AnotherIterator {Новая версия итератора позволяет явно выразить тот факт, что мы не можем вызвать
type Item<'a>; // обратите внимание: тип параметризован
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
// комбинаторы поверх next
}
next
два раза подряд, сохранив оба результата. Да, нам придётся реализовать комбинаторы заново, но этот будут общие методы, а не функции под конкретный тип, которые плохо композируются друг с другом.trait OneMoreiterator<'a> {Можно. Но это решение обладает рядом недостатков. Первое: его неудобно использовать на практике. Так как нужный нам лайфтайм
type Item;
fn next(&'a mut self) -> Option<&'a Self::Item>;
}
'a
, скорее всего, будет локальным, а не обобщённым параметром, нам придётся использовать HRTB и писать что-то вродеfn do_stuff<I>(iter: I)Во-вторых — и это уже куда как более сильное ограничение — данное определение позволяет возвращать из
where
I: for<'a> OneMoreIterator<'a>,
{
//...
}
next
лишь ссылку на Item
, а не произвольный тип, параметризованный лайфтаймом (скажем, я могу себе представить тип, реализующий AnotherIterator
и возвращающий MutexGuard). В качестве примера, демонстрирующего оба недостатка, можно назвать WindowsMut
, несуществующий пока аналог Windows, возвращающий перекрывающиеся мутабельные подслайсы исходного слайса.