From 4c4d8b8ccd0c4e3bb0597d7209131e96ae1af04e Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Mon, 6 Jul 2026 15:57:00 +0200 Subject: [PATCH] lix-rs: enable iterators we don't support range-based iteration yet because it's a huge hassle to make c++ and rust iteration styles meet in any reasonable way. this is a good start to make rust collections *actually* usable though, so here we go. `into_iter` is not explicitly supported, but can be used regardless. Change-Id: Iebb76c9409059bc81623e5be81ac2656b1ec5138 --- lix/lix-rs/primitives.zng | 3 ++ lix/lix-rs/std-common.zng | 58 ++++++++++++++++++++++++++++ lix/lix-rs/utils.hh | 77 ++++++++++++++++++++++++++++++++++++++ tests/unit/libutil/rust.cc | 28 ++++++++++++++ 4 files changed, 166 insertions(+) diff --git a/lix/lix-rs/primitives.zng b/lix/lix-rs/primitives.zng index 8f57627b9..ad882539c 100644 --- a/lix/lix-rs/primitives.zng +++ b/lix/lix-rs/primitives.zng @@ -11,6 +11,9 @@ type [u8] { fn as_ptr(&self) -> *const u8; fn len(&self) -> usize; + + fn iter(&self) -> ::std::slice::Iter; + fn iter_mut(&mut self) -> ::std::slice::IterMut; } type str { diff --git a/lix/lix-rs/std-common.zng b/lix/lix-rs/std-common.zng index 4e79c130c..1f80122dc 100644 --- a/lix/lix-rs/std-common.zng +++ b/lix/lix-rs/std-common.zng @@ -44,6 +44,33 @@ mod ::std::option { fn unwrap(self) -> String; } + + type Option { + #layout_conservative(size = 2, align = 1); + + constructor Some(u8); + constructor None; + + fn unwrap(self) -> u8; + } + + type Option<&u8> { + #layout_conservative(size = 8, align = 8); + + constructor Some(&u8); + constructor None; + + fn unwrap(self) -> &u8; + } + + type Option<&mut u8> { + #layout_conservative(size = 8, align = 8); + + constructor Some(&mut u8); + constructor None; + + fn unwrap(self) -> &mut u8; + } } mod ::std::result { @@ -60,6 +87,20 @@ mod ::std::result { } } +mod ::std::slice { + type Iter { + #layout_conservative(size = 16, align = 8); + + fn next(&mut self) -> ::std::option::Option<&u8>; + } + + type IterMut { + #layout_conservative(size = 16, align = 8); + + fn next(&mut self) -> ::std::option::Option<&mut u8>; + } +} + mod ::std::string { type String { #layout_conservative(size = 24, align = 8); @@ -79,4 +120,21 @@ mod ::std::vec { fn push(&mut self, String); fn len(&self) -> usize; } + + type Vec { + #layout_conservative(size = 24, align = 8); + + fn new() -> Vec; + fn push(&mut self, u8); + fn len(&self) -> usize; + fn as_ref(&self) -> &[u8]; + fn as_mut(&mut self) -> &mut [u8]; + fn into_iter(self) -> IntoIter; + } + + type IntoIter { + #layout_conservative(size = 32, align = 8); + + fn next(&mut self) -> ::std::option::Option; + } } diff --git a/lix/lix-rs/utils.hh b/lix/lix-rs/utils.hh index aba139821..28684d8c5 100644 --- a/lix/lix-rs/utils.hh +++ b/lix/lix-rs/utils.hh @@ -224,6 +224,73 @@ concept HasOpEq = requires(Lhs lhs, Rhs rhs) { Ref<::std::remove_cvref_t>{}; Ref<::std::remove_cvref_t>{}; }; + +template +struct OptionArg; +template +struct OptionArg> +{ + using type = T; +}; + +template +concept IsOption = requires { typename OptionArg::type; }; + +template +concept Iterator = requires(T t) { + { t.next() } -> IsOption; +}; + +namespace detail { +struct iterator_end +{}; + +template +class iterator +{ + using step_type = decltype(to_std(::std::declval().next())); + + Iter rs; + step_type current; + +public: + using iterator_category = ::std::input_iterator_tag; + using difference_type = void; + using value_type = step_type::value_type; + using reference = value_type &; + using pointer = value_type *; + + explicit iterator(Iter rs) : rs(::std::move(rs)) + { + ++*this; + } + + pointer operator->() + { + return &*current; + } + reference operator*() + { + return *current; + } + + iterator & operator++() + { + current = to_std(rs.next()); + return *this; + } + + void operator++(int) + { + ++*this; + } + + bool operator==(iterator_end) const + { + return !current.has_value(); + } +}; +} } // clang-format off @@ -245,7 +312,17 @@ concept HasOpEq = requires(Lhs lhs, Rhs rhs) { template requires ::rust::HasOpEq \ bool operator!=(const Lhs & lhs, const Rhs & rhs) { return !bool(lhs.eq(rhs)); } \ } +#define LIX_DECLARE_ITERATORS(ns) \ + namespace ns { \ + template<::rust::Iterator T> \ + auto begin(T & rs) { return ::rust::detail::iterator{::std::move(rs)}; } \ + template<::rust::Iterator T> \ + auto end(const T & rs) { return ::rust::detail::iterator_end{}; } \ + } // clang-format on LIX_DECLARE_ORD_OPS(rust::lix::ffi_test) LIX_DECLARE_EQ_OPS(rust::lix::ffi_test) + +LIX_DECLARE_ITERATORS(rust::std::slice) +LIX_DECLARE_ITERATORS(rust::std::vec) diff --git a/tests/unit/libutil/rust.cc b/tests/unit/libutil/rust.cc index ed7db9e1e..ecef89042 100644 --- a/tests/unit/libutil/rust.cc +++ b/tests/unit/libutil/rust.cc @@ -6,8 +6,10 @@ #include "gtest/gtest.h" #include #include +#include #include #include +#include namespace nix { @@ -122,4 +124,30 @@ TEST(rustSupport, testOperators) ASSERT_EQ(set.size(), 1); } + +TEST(rustSupport, iterators) +{ + auto vec = rust::Vec::new_(); + vec.push(1); + vec.push(2); + vec.push(3); + vec.push(4); + vec.push(5); + + int i = 0; + for (auto u : vec.as_ref().iter()) { + EXPECT_EQ(*u, ++i); + } + for (auto u : vec.as_mut().iter_mut()) { + (*u)++; + } + i = 1; + for (auto u : vec.as_ref().iter()) { + EXPECT_EQ(*u, ++i); + } + i = 1; + for (auto u : vec.into_iter()) { + EXPECT_EQ(u, ++i); + } +} }