{"id":17644433,"url":"https://github.com/vigna/hrtb-lending-iterator-rs","last_synced_at":"2025-09-06T18:41:59.038Z","repository":{"id":200451452,"uuid":"705527603","full_name":"vigna/hrtb-lending-iterator-rs","owner":"vigna","description":"A lending iterator trait based on generic associated types and higher-rank trait bounds","archived":false,"fork":false,"pushed_at":"2023-10-30T07:39:03.000Z","size":88,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-31T05:03:02.381Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vigna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-Apache-2.0","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-16T07:31:21.000Z","updated_at":"2024-02-24T09:55:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"eb538b11-dbd7-490b-b077-5a7652e61703","html_url":"https://github.com/vigna/hrtb-lending-iterator-rs","commit_stats":null,"previous_names":["vigna/hrtb-lending-iterator-rs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vigna%2Fhrtb-lending-iterator-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vigna%2Fhrtb-lending-iterator-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vigna%2Fhrtb-lending-iterator-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vigna%2Fhrtb-lending-iterator-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vigna","download_url":"https://codeload.github.com/vigna/hrtb-lending-iterator-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252798152,"owners_count":21805837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-23T10:05:25.890Z","updated_at":"2025-05-07T01:41:50.812Z","avatar_url":"https://github.com/vigna.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WARNING\n\nHalfway through the development of this crate we learned about [Lender](https://crates.io/crates/lender),\nwhich has exactly the same goals of this crate, uses the same ideas, and is much more developed, so\nwe joined the development team. This crate is thus unmaintained and this document is here for historical reasons only.\n\n## A lending iterator trait based on higher-rank trait bounds (HRTBs)\n\nA *lending iterator* is an iterator which lends mutable borrows to the items it returns.\nIn particular, this means that the reference to an item is invalidated by the \nnext call to `next()`.\n\nThe typical example that cannot\nbe written with standard Rust iterators, but is covered by lending iterators,\nis that of an iterator returning mutable, overlapping windows\nof a slice.\n\nBut lending iterators are more general than that, as they\nmight return items that depend on some mutable state stored in the iterator. For example, a\nlending iterator might return references to the lines of a file reusing an internal buffer;\nalso, starting from an iterator on pairs of integers lexicographically sorted, a lending iterator might return\niterators on pairs with the same first coordinate without any copying; clearly, in all these cases\nany call on `next()` would invalidate the reference returned by the previous call.\n\nSimilarly to what happens with standard iterators, besides the fundamental [`LendingIterator`] trait \nthere is a [`IntoLendingIterator`] trait\nand methods such as [`LendingIterator::map`]. Our aim is to have a library as complete as that\nof standard iterators, but there is still a lot of work to do.\n\nThe Rust syntax for iterating over types implementing [`IntoIterator`] cannot be extended\nto lending iterators. The idiomatic way of iterate over a lending iterator is to use\na `while let` loop, as in:\n```ignore\nwhile let Some(item) = iter.next() {\n    // Do something with item\n}\n```\nNote that if you have a variable `x` with an `iter` method returning a lending iterator,\nyou cannot use the form `while let Some(item) = x.iter().next()` as you will iterate\nover the first element forever.\n\nTo make iteration simpler, we provide a macro [`for_lend!`] that can be used to iterate in a\nway more similar to a `for` loop.\n\n## An example: reusing line buffers\n\nThe following code shows how to implement a lending iterator returning lines from a file,\nreusing a buffer for the line:\n```rust\nuse hrtb_lending_iterator::*;\nuse std::fs::File;\nuse std::io::{BufRead, BufReader};\n\nstruct Lines {\n    reader: BufReader\u003cFile\u003e,\n    buffer: String,\n}\n\nimpl\u003c'any\u003e LendingIteratorItem\u003c'any\u003e for Lines {\n    type Type = \u0026'any str;\n}\n\nimpl LendingIterator for Lines {\n    fn next(\u0026mut self) -\u003e Option\u003cItem\u003c'_, Self\u003e\u003e {\n        self.buffer.clear();\n        if self.reader.read_line(\u0026mut self.buffer).unwrap() == 0 {\n            return None;\n        }\n        Some(\u0026self.buffer)\n    }\n}\n\nfn main() {\n    let mut iter = Lines {\n        reader: BufReader::new(File::open(\"Cargo.toml\").unwrap()),\n        buffer: String::new(),\n    };\n    while let Some(line) = iter.next() {\n        // line is a reference to the buffer\n        print!(\"{}\", line);\n    }\n}\n```\nSince the library contains several methods analogous to those of Rust iterators, you can \nenumerate just at most the first ten lines with \n```ignore\n    let mut iter = Lines {\n        reader: BufReader::new(File::open(\"Cargo.toml\").unwrap()),\n        buffer: String::new(),\n    }.take(10);\n```\n\nMoreover, if at any time you decide that you prefer to handle owned strings, you have just\nto turn the lending iterator into a standard iterator by making the returned items owned:\n```ignore\n    for line in iter.to_owned_item() {\n        // line is a copy of the buffer\n        print!(\"{}\", line);\n    }\n```\nThis is possible every time that the type referenced by the returned item implements\n[`ToOwned`](std::borrow::ToOwned).\n\n## An example: overlapping windows\n\nThe following code shows how to implement a lending iterator returning overlapping windows\nof a slice:\n```rust\nuse hrtb_lending_iterator::*;\n\nstruct WindowsMut\u003c'a, T, const WINDOW_SIZE: usize\u003e {\n    slice: \u0026'a mut [T],\n    curr_pos: usize,\n}\n\nimpl\u003c'a, 'any, T, const WINDOW_SIZE: usize\u003e LendingIteratorItem\u003c'any\u003e\n    for WindowsMut\u003c'a, T, WINDOW_SIZE\u003e\n{\n    type Type = \u0026'any mut [T; WINDOW_SIZE];\n}\n\nimpl\u003c'a, T, const WINDOW_SIZE: usize\u003e LendingIterator for WindowsMut\u003c'a, T, WINDOW_SIZE\u003e {\n    fn next(\u0026mut self) -\u003e Option\u003cItem\u003c'_, Self\u003e\u003e {\n        let window = self\n            .slice\n            .get_mut(self.curr_pos..)?\n            .get_mut(..WINDOW_SIZE)?;\n        self.curr_pos += 1;\n        Some(window.try_into().unwrap())\n    }\n}\n\nfn main() {\n    let mut v = vec![1, 2, 3, 4, 5];\n    let mut iter = WindowsMut::\u003c'_, _, 3\u003e {\n        slice: \u0026mut v,\n        curr_pos: 0,\n    };\n    while let Some(window) = iter.next() {\n        // The window is mutable\n        window[0] = window[2] - window[1];\n        println!(\"{:?}\", window);\n    }\n}\n```\nIn fact, this is already done for you using an [extension trait](SliceExt::windows_mut), so you can just use it:\n```rust\nuse hrtb_lending_iterator::*;\n\nfn main() {\n    let mut v = vec![1, 2, 3, 4, 5];\n    let mut iter = v.windows_mut::\u003c3\u003e();\n    while let Some(window) = iter.next() {\n        // The window is mutable\n        window[0] = window[2] - window[1];\n        println!(\"{:?}\", window);\n    }\n}\n```\n\n\n## Interacting with standard iterators\n\nThe library provides several methods that make it possible to move\nfrom world of standard iterator to the world of lending iterators and vice versa.\n\n- All types implementing [`Iterator`] can be turned into a [`LendingIterator`]\n  by calling the method [`Iterator::into_lend_iter`](IteratorExt::into_lend_iter),\n  and all types implementing [`IntoIterator`] can be turned into a [`IntoLendingIterator`]\n  by calling the method [`IntoIterator::into_into_lend_iter`](IntoIteratorExt::into_into_lend_iter).\n  This is achieved via trait extension, but the methods are\n  also available as free functions [`from_iter`](crate::from_iter) and\n  [`from_into_iter`](crate::from_into_iter). These conversions happen without allocation.\n\n- If a lending iterator is actually a standard iterator because there is no actual borrow,\n  the method [`LendingIterator::into_iter`] can be used to turn it into a lending iterator,\n  and the same happens with [`IntoLendingIterator::into_into_iter`](crate::IntoLendingIterator::into_into_iter). These conversions happens without allocation, and are the inverses of the previous two.\n\n- The method [`LendingIterator::to_owned_item`] turns a lending iterator into a standard iterator\n  returning owned items. This is possible every time that the type referenced by the returned\n  item implements [`ToOwned`](std::borrow::ToOwned). There will be allocation if the\n  [`ToOwned::to_owned`] method allocates when applied to each item.\n\n## Type-inference problems\n\nDue to the complex type dependencies and higher-kind trait bounds\ninvolved, the current Rust compiler cannot\nalways infer the correct type of a lending iterator and of the items it returns.\nIn general, when writing methods accepting a [`LendingIterator`]\nrestricting the returned item type with a *type* will work, as in:\n\n```rust\nuse hrtb_lending_iterator::*;\n\nstruct MockLendingIterator {}\n\nimpl\u003c'any\u003e LendingIteratorItem\u003c'any\u003e for MockLendingIterator {\n    type Type = \u0026'any str;\n}\n\nimpl LendingIterator for MockLendingIterator {\n    fn next(\u0026mut self) -\u003e Option\u003cItem\u003c'_, Self\u003e\u003e {\n        None\n    }\n}\n\nfn read_lend_iter\u003cL\u003e(iter: L)\nwhere\n    L: LendingIterator + for\u003c'any\u003e LendingIteratorItem\u003c'any, Type = \u0026'any str\u003e,\n{}\n\nfn test_mock_lend_iter(m: MockLendingIterator) {\n    read_lend_iter(m);\n}\n```\n\nHowever, the following code, which restricts the returned items using a trait bound,\ndoes not compile as of Rust 1.73.0:\n\n```ignore\nuse hrtb_lending_iterator::*;\n\nstruct MockLendingIterator {}\n\nimpl\u003c'any\u003e LendingIteratorItem\u003c'any\u003e for MockLendingIterator {\n    type Type = \u0026'any str;\n}\n\nimpl LendingIterator for MockLendingIterator {\n    fn next(\u0026mut self) -\u003e Option\u003cItem\u003c'_, Self\u003e\u003e {\n        None\n    }\n}\n\nfn read_lend_iter\u003cL\u003e(iter: L)\nwhere\n    L: LendingIterator,\n    for\u003c'any\u003e \u003cL as LendingIteratorItem\u003c'any\u003e\u003e::Type: AsRef\u003cstr\u003e,\n{}\n\nfn test_mock_lend_iter(m: MockLendingIterator) {\n    read_lend_iter(\u0026m);\n}\n```\n\nThe workaround is to use an explicit type annotation:\n\n```rust\nuse hrtb_lending_iterator::*;\n\nstruct MockLendingIterator {}\n\nimpl\u003c'any\u003e LendingIteratorItem\u003c'any\u003e for MockLendingIterator {\n    type Type = \u0026'any str;\n}\n\nimpl LendingIterator for MockLendingIterator {\n    fn next(\u0026mut self) -\u003e Option\u003cItem\u003c'_, Self\u003e\u003e {\n        None\n    }\n}\n\nfn read_lend_iter\u003cL\u003e(iter: L)\nwhere\n    L: LendingIterator,\n    for\u003c'any\u003e \u003cL as LendingIteratorItem\u003c'any\u003e\u003e::Type: AsRef\u003cstr\u003e,\n{}\n\nfn test_mock_lend_iter(m: MockLendingIterator) {\n    read_lend_iter::\u003cMockLendingIterator\u003e(m);\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvigna%2Fhrtb-lending-iterator-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvigna%2Fhrtb-lending-iterator-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvigna%2Fhrtb-lending-iterator-rs/lists"}