lender
🙂Lending Iterator; a niche, yet seemingly pervasive, antipattern[^1]. This crate provides one such implementation, 'utilizing' #84533 and #25860.
Ok, maybe 'antipattern' is a little tough, but let's spare the antagonizing examples, if you can avoid using lending iterators, you probably should. You should heed the counsel of Polonious: "Neither a borrower nor a lender be".
Forewarning, before you go on with this crate, you should consider using a more seasoned 'lending iterator' crate, like the [lending-iterator
] or [streaming-iterator
] crates.
Also, if a dyn Lender
trait object is in your future, this crate definitely isn't going to work.
This crate was not made to be used in any sort of production code, so please, use at your own risk (Documentation be damned!).
Nevertheless, to begin, I present to you WindowsMut
.
```rust use ::lender::prelude::*;
struct WindowsMut<'a, T> { slice: &'a mut [T], begin: usize, len: usize, } impl<'lend, 'a, T> Lending<'lend> for WindowsMut<'a, T> { type Lend = &'lend mut [T]; } impl<'a, T> Lender for WindowsMut<'a, T> { fn next<'lend>(&'lend mut self) -> Option<&'lend mut [T]> { let begin = self.begin; self.begin = self.begin.saturatingadd(1); self.slice.getmut(begin..begin + self.len) } } // Fibonacci sequence let mut data = vec![0u32; 3 * 3]; data[1] = 1; WindowsMut { slice: &mut data, begin: 0, len: 3 } .foreach(hrcmut!(for<'lend> |w: &'lend mut [u32]| { w[2] = w[0] + w[1] })); assert_eq!(data, [0, 1, 1, 2, 3, 5, 8, 13, 21]); ```
As all great standard examples are, a WindowsMut
just for a Fibonacci sequence is actually a great example of what you shouldn't use lending iterators for.
Libraries can just provide Index
and IndexMut
on their collections and it's a lot of boilerplate for something a simple for loop can do.
rust
let mut data = vec![0; 3 * 3];
data[1] = 1;
for i in 2..data.len() {
data[i] = data[i - 1] + data[i - 2];
}
assert_eq!(data, [0, 1, 1, 2, 3, 5, 8, 13, 21]);
So, let's look at a slightly more interesting example, LinesStr
, an io::Lines
with an Item
of &str
instead of String
.
It's a good example of borrowing from the iterator itself.
```rust use std::io; use ::lender::prelude::*;
struct LinesStr {
buf: B,
line: String,
}
impl<'lend, B: io::BufRead> Lending<'lend> for LinesStr {
type Lend = io::Result<&'lend str>;
}
impl
let buf = io::BufReader::withcapacity(10, "Hello\nWorld\n".asbytes()); let mut lines = LinesStr { buf, line: String::new() }; asserteq!(lines.next().unwrap().unwrap(), "Hello"); asserteq!(lines.next().unwrap().unwrap(), "World"); ```
For most cases like this, you could just probably rely on the optimizer, i.e. reusing the same buffer instead of allocating a new one each time, but you see where we're going with this.
Turn a lender into an iterator with cloned()
where lend is Clone
, copied()
where lend is Copy
, owned()
where lend is ToOwned
, or iter()
where lend already satisfies the restrictions of Iterator::Item
.
partition_in_place
and array_chunks
are unsupported. Instead of array_chunks
, we have chunky
, to make lenders nice and chunky 🙂.
Please thank and check out the great resources below that helped me and many others learn about Rust and the lending iterator problem.
lending-iterator
] and many other great crates and sharing their great work.Many patterns in lending iterators require polonius-emulating unsafe code, but please, if you see any unsafe code that can be made safe, please let me know! I am still learning Rust and I'm sure I've made many mistakes.
Licensed under either the MIT or Apache-2.0 license.