This is a small proof of concept showing a port of Rust iterator interface to modern C++. Is it better than C++ stdlib iterators? Probably not, but it is cool.
Any contributions are welcome, but please adhere to our code of conduct.
We utilize conventional commits. We don't maintain a changelog manually. Squashed commits, keeping the correct convention should be more than enough to create a release summary.
We use both clang-tidy and clang-format. The former is integrated with CMake and will be run during build. The latter can be run using provided formatting script.
./tools/format.shWe provide a small utility script to build the project. We use both CMake and Ninja, so make sure you have those installed.
# Clean build:
./tools/build.sh --no-cache --compile-tests
# Build with specified compiler:
./tools/build.sh --compiler g++-14 --compile-tests --no-cache
# Cached build:
./tools/build.sh --compile-tests
# Build in release mode
./tools/build.sh --compile-tests --releaseSome actual real-life inspired examples can be found in the examples/ directory. You can build them using provided build script:
./tools/build.sh --compile-examplesHere we will list some of the simple examples, to show you the power of lazy iteration!
auto data = std::vector{1, 2, 3, 4, 5, 6};
auto result = LazyIterator{data}
.filter([](auto x) { return x % 2 == 0; })
.count();auto data = std::vector{1, 2, 3};
auto result = LazyIterator{data}.cycle().take(10).collect();auto data = std::vector{1, 2, 3, 4};
auto f = [](auto x){ ... } // Do some business logic.
LazyIterator{data}.forEach(std::move(f));auto data = std::vector<std::string>{"a", "abc", "defg"};
auto result = LazyIterator{data}
.map([](auto x) { return x.get().size(); })
.min();auto data = std::vector{1, 2, 3, 4};
auto result = LazyIterator{data}
.filter([](auto x) { return x % 2 != 0; })
.map([](auto x) { return x * x; })
.collect();auto v1 = std::vector{1, 2, 3};
auto v2 = std::vector{4, 5, 6};
auto result = LazyIterator{v1}
.chain(LazyIterator{v2}
.map([](auto x) { return x * 2; }))
.collect();auto v1 = std::vector{1, 2, 3, 4, 5};
auto v2 = std::vector{2, 7, 4, 8, 1};
auto result = LazyIterator{v1}
.zip(LazyIterator{v2})
.map([](auto x) { return std::abs(std::get<0>(x) - std::get<1>(x)); })
.max();auto fileName = std::string{"test.txt"};
auto numbers = FileIterator<FIterType::Lazy>{fileName}
.filter([](auto x) { return std::all_of(x.begin(), x.end(), std::isdigit); })
.map([](auto x) { return std::atoi(x.c_str()); })
.collect();We provide a small set of benchmarks located in the benchmarks/ directory. You can build them using provided build script.
./tools/build.sh --compile-benchmarksAll of the benchmarks measure the performance in release mode and run similar scenario using C++20 std::ranges (if it is possible to recreate tested feature without writing a lot of code) to compare how fast, or how slow we are.
All of the measurements were taken on MacBook M3 Pro 18GB.
Apply filter and map on 1'000'000 integers
| Benchmark | Time [ns] | CPU [ns] |
|---|---|---|
| benchmarkRustyIterFilterAndMap | 559108 | 559065 |
| benchmarkRustyIterFilterMap | 556122 | 556120 |
| benchmarkRangesFilterTransform | 539868 | 539786 |
Apply filter and map on 10'000'000 integers
| Benchmark | Time [ns] | CPU [ns] |
|---|---|---|
| benchmarkRustyIterFilterAndMap | 5644164 | 5643854 |
| benchmarkRustyIterFilterMap | 5599177 | 5599185 |
| benchmarkRangesFilterTransform | 5988982 | 5988701 |