Implement rust repeated scalars for cpp and upb

PiperOrigin-RevId: 574261929
diff --git a/rust/BUILD b/rust/BUILD
index dd23b44..3e0ca71 100644
--- a/rust/BUILD
+++ b/rust/BUILD
@@ -52,6 +52,7 @@
     "optional.rs",
     "primitive.rs",
     "proxied.rs",
+    "repeated.rs",
     "shared.rs",
     "string.rs",
     "vtable.rs",
@@ -92,8 +93,14 @@
     name = "protobuf_cpp",
     srcs = PROTOBUF_SHARED + ["cpp.rs"],
     crate_root = "shared.rs",
+    proc_macro_deps = [
+        "@crate_index//:paste",
+    ],
     rustc_flags = ["--cfg=cpp_kernel"],
-    deps = [":utf8"],
+    deps = [
+        ":utf8",
+        "//rust/cpp_kernel:cpp_api",
+    ],
 )
 
 rust_test(
diff --git a/rust/cpp.rs b/rust/cpp.rs
index c2af02d..6ef1240 100644
--- a/rust/cpp.rs
+++ b/rust/cpp.rs
@@ -7,7 +7,8 @@
 
 // Rust Protobuf runtime using the C++ kernel.
 
-use crate::__internal::{Private, RawArena, RawMessage};
+use crate::__internal::{Private, RawArena, RawMessage, RawRepeatedField};
+use paste::paste;
 use std::alloc::Layout;
 use std::cell::UnsafeCell;
 use std::fmt;
@@ -35,6 +36,7 @@
 impl Arena {
     /// Allocates a fresh arena.
     #[inline]
+    #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
         Self { ptr: NonNull::dangling(), _not_sync: PhantomData }
     }
@@ -182,6 +184,116 @@
     val
 }
 
+/// RepeatedField impls delegate out to `extern "C"` functions exposed by
+/// `cpp_api.h` and store either a RepeatedField* or a RepeatedPtrField*
+/// depending on the type.
+///
+/// Note: even though this type is `Copy`, it should only be copied by
+/// protobuf internals that can maintain mutation invariants:
+///
+/// - No concurrent mutation for any two fields in a message: this means
+///   mutators cannot be `Send` but are `Sync`.
+/// - If there are multiple accessible `Mut` to a single message at a time, they
+///   must be different fields, and not be in the same oneof. As such, a `Mut`
+///   cannot be `Clone` but *can* reborrow itself with `.as_mut()`, which
+///   converts `&'b mut Mut<'a, T>` to `Mut<'b, T>`.
+#[derive(Clone, Copy)]
+pub struct RepeatedField<'msg, T: ?Sized> {
+    inner: RepeatedFieldInner<'msg>,
+    _phantom: PhantomData<&'msg mut T>,
+}
+
+/// CPP runtime-specific arguments for initializing a RepeatedField.
+/// See RepeatedField comment about mutation invariants for when this type can
+/// be copied.
+#[derive(Clone, Copy)]
+pub struct RepeatedFieldInner<'msg> {
+    pub raw: RawRepeatedField,
+    pub _phantom: PhantomData<&'msg ()>,
+}
+
+impl<'msg, T: ?Sized> RepeatedField<'msg, T> {
+    pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+        RepeatedField { inner, _phantom: PhantomData }
+    }
+}
+impl<'msg> RepeatedField<'msg, i32> {}
+
+pub trait RepeatedScalarOps {
+    fn new_repeated_field() -> RawRepeatedField;
+    fn push(f: RawRepeatedField, v: Self);
+    fn len(f: RawRepeatedField) -> usize;
+    fn get(f: RawRepeatedField, i: usize) -> Self;
+    fn set(f: RawRepeatedField, i: usize, v: Self);
+}
+
+macro_rules! impl_repeated_scalar_ops {
+    ($($t: ty),*) => {
+        paste! { $(
+            extern "C" {
+                fn [< __pb_rust_RepeatedField_ $t _new >]() -> RawRepeatedField;
+                fn [< __pb_rust_RepeatedField_ $t _add >](f: RawRepeatedField, v: $t);
+                fn [< __pb_rust_RepeatedField_ $t _size >](f: RawRepeatedField) -> usize;
+                fn [< __pb_rust_RepeatedField_ $t _get >](f: RawRepeatedField, i: usize) -> $t;
+                fn [< __pb_rust_RepeatedField_ $t _set >](f: RawRepeatedField, i: usize, v: $t);
+            }
+            impl RepeatedScalarOps for $t {
+                fn new_repeated_field() -> RawRepeatedField {
+                    unsafe { [< __pb_rust_RepeatedField_ $t _new >]() }
+                }
+                fn push(f: RawRepeatedField, v: Self) {
+                    unsafe { [< __pb_rust_RepeatedField_ $t _add >](f, v) }
+                }
+                fn len(f: RawRepeatedField) -> usize {
+                    unsafe { [< __pb_rust_RepeatedField_ $t _size >](f) }
+                }
+                fn get(f: RawRepeatedField, i: usize) -> Self {
+                    unsafe { [< __pb_rust_RepeatedField_ $t _get >](f, i) }
+                }
+                fn set(f: RawRepeatedField, i: usize, v: Self) {
+                    unsafe { [< __pb_rust_RepeatedField_ $t _set >](f, i, v) }
+                }
+            }
+        )* }
+    };
+}
+
+impl_repeated_scalar_ops!(i32, u32, i64, u64, f32, f64, bool);
+
+impl<'msg, T: RepeatedScalarOps> RepeatedField<'msg, T> {
+    #[allow(clippy::new_without_default, dead_code)]
+    /// new() is not currently used in our normal pathways, it is only used
+    /// for testing. Existing `RepeatedField<>`s are owned by, and retrieved
+    /// from, the containing `Message`.
+    pub fn new() -> Self {
+        Self::from_inner(
+            Private,
+            RepeatedFieldInner::<'msg> { raw: T::new_repeated_field(), _phantom: PhantomData },
+        )
+    }
+    pub fn push(&mut self, val: T) {
+        T::push(self.inner.raw, val)
+    }
+    pub fn len(&self) -> usize {
+        T::len(self.inner.raw)
+    }
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+    pub fn get(&self, index: usize) -> Option<T> {
+        if index >= self.len() {
+            return None;
+        }
+        Some(T::get(self.inner.raw, index))
+    }
+    pub fn set(&mut self, index: usize, val: T) {
+        if index >= self.len() {
+            return;
+        }
+        T::set(self.inner.raw, index, val)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -201,4 +313,27 @@
         let serialized_data = SerializedData { data: NonNull::new(ptr).unwrap(), len: len };
         assert_eq!(&*serialized_data, b"Hello world");
     }
+
+    #[test]
+    fn repeated_field() {
+        let mut r = RepeatedField::<i32>::new();
+        assert_eq!(r.len(), 0);
+        r.push(32);
+        assert_eq!(r.get(0), Some(32));
+
+        let mut r = RepeatedField::<u32>::new();
+        assert_eq!(r.len(), 0);
+        r.push(32);
+        assert_eq!(r.get(0), Some(32));
+
+        let mut r = RepeatedField::<f64>::new();
+        assert_eq!(r.len(), 0);
+        r.push(0.1234f64);
+        assert_eq!(r.get(0), Some(0.1234));
+
+        let mut r = RepeatedField::<bool>::new();
+        assert_eq!(r.len(), 0);
+        r.push(true);
+        assert_eq!(r.get(0), Some(true));
+    }
 }
diff --git a/rust/cpp_kernel/BUILD b/rust/cpp_kernel/BUILD
index 245772c..d10f9e7 100644
--- a/rust/cpp_kernel/BUILD
+++ b/rust/cpp_kernel/BUILD
@@ -4,13 +4,14 @@
 
 cc_library(
     name = "cpp_api",
+    srcs = ["cpp_api.cc"],
     hdrs = ["cpp_api.h"],
     visibility = [
         "//src/google/protobuf:__subpackages__",
         "//rust:__subpackages__",
     ],
     deps = [
-        ":rust_alloc_for_cpp_api",
+        ":rust_alloc_for_cpp_api",  # buildcleaner: keep
         "//:protobuf_nowkt",
     ],
 )
diff --git a/rust/cpp_kernel/cpp_api.cc b/rust/cpp_kernel/cpp_api.cc
new file mode 100644
index 0000000..8ff79d8
--- /dev/null
+++ b/rust/cpp_kernel/cpp_api.cc
@@ -0,0 +1,35 @@
+#include "google/protobuf/repeated_field.h"
+
+extern "C" {
+
+#define expose_repeated_field_methods(ty, rust_ty)                           \
+  google::protobuf::RepeatedField<ty>* __pb_rust_RepeatedField_##rust_ty##_new() {     \
+    return new google::protobuf::RepeatedField<ty>();                                  \
+  }                                                                          \
+  void __pb_rust_RepeatedField_##rust_ty##_add(google::protobuf::RepeatedField<ty>* r, \
+                                               ty val) {                     \
+    r->Add(val);                                                             \
+  }                                                                          \
+  size_t __pb_rust_RepeatedField_##rust_ty##_size(                           \
+      google::protobuf::RepeatedField<ty>* r) {                                        \
+    return r->size();                                                        \
+  }                                                                          \
+  ty __pb_rust_RepeatedField_##rust_ty##_get(google::protobuf::RepeatedField<ty>* r,   \
+                                             size_t index) {                 \
+    return r->Get(index);                                                    \
+  }                                                                          \
+  void __pb_rust_RepeatedField_##rust_ty##_set(google::protobuf::RepeatedField<ty>* r, \
+                                               size_t index, ty val) {       \
+    return r->Set(index, val);                                               \
+  }
+
+expose_repeated_field_methods(int32_t, i32);
+expose_repeated_field_methods(uint32_t, u32);
+expose_repeated_field_methods(float, f32);
+expose_repeated_field_methods(double, f64);
+expose_repeated_field_methods(bool, bool);
+expose_repeated_field_methods(uint64_t, u64);
+expose_repeated_field_methods(int64_t, i64);
+
+#undef expose_repeated_field_methods
+}
diff --git a/rust/internal.rs b/rust/internal.rs
index 1e0f536..e56c9dc 100644
--- a/rust/internal.rs
+++ b/rust/internal.rs
@@ -51,6 +51,17 @@
         _data: [u8; 0],
         _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
     }
+
+    /// Opaque pointee for [`RawRepeatedField`]
+    ///
+    /// This type is not meant to be dereferenced in Rust code.
+    /// It is only meant to provide type safety for raw pointers
+    /// which are manipulated behind FFI.
+    #[repr(C)]
+    pub struct RawRepeatedFieldData {
+        _data: [u8; 0],
+        _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+    }
 }
 
 /// A raw pointer to the underlying message for this runtime.
@@ -59,6 +70,9 @@
 /// A raw pointer to the underlying arena for this runtime.
 pub type RawArena = NonNull<_opaque_pointees::RawArenaData>;
 
+/// A raw pointer to the underlying repeated field container for this runtime.
+pub type RawRepeatedField = NonNull<_opaque_pointees::RawRepeatedFieldData>;
+
 /// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
 /// borrowed slice of bytes) for FFI use only.
 ///
diff --git a/rust/repeated.rs b/rust/repeated.rs
new file mode 100644
index 0000000..b824de8
--- /dev/null
+++ b/rust/repeated.rs
@@ -0,0 +1,119 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+
+/// Repeated scalar fields are implemented around the runtime-specific
+/// `RepeatedField` struct. `RepeatedField` stores an opaque pointer to the
+/// runtime-specific representation of a repeated scalar (`upb_Array*` on upb,
+/// and `RepeatedField<T>*` on cpp).
+use std::marker::PhantomData;
+
+use crate::{
+    __internal::{Private, RawRepeatedField},
+    __runtime::{RepeatedField, RepeatedFieldInner},
+};
+
+#[derive(Clone, Copy)]
+pub struct RepeatedFieldRef<'a> {
+    pub repeated_field: RawRepeatedField,
+    pub _phantom: PhantomData<&'a mut ()>,
+}
+
+unsafe impl<'a> Send for RepeatedFieldRef<'a> {}
+unsafe impl<'a> Sync for RepeatedFieldRef<'a> {}
+
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct RepeatedView<'a, T: ?Sized> {
+    inner: RepeatedField<'a, T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedView<'msg, T> {
+    pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+        Self { inner: RepeatedField::<'msg>::from_inner(_private, inner) }
+    }
+}
+
+pub struct RepeatedFieldIter<'a, T> {
+    inner: RepeatedField<'a, T>,
+    current_index: usize,
+}
+
+impl<'a, T> std::fmt::Debug for RepeatedView<'a, T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_tuple("RepeatedView").finish()
+    }
+}
+
+#[repr(transparent)]
+pub struct RepeatedMut<'a, T: ?Sized> {
+    inner: RepeatedField<'a, T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedMut<'msg, T> {
+    pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+        Self { inner: RepeatedField::from_inner(_private, inner) }
+    }
+}
+
+impl<'a, T> std::ops::Deref for RepeatedMut<'a, T> {
+    type Target = RepeatedView<'a, T>;
+    fn deref(&self) -> &Self::Target {
+        // SAFETY:
+        //   - `Repeated{View,Mut}<'a, T>` are both `#[repr(transparent)]` over
+        //     `RepeatedField<'a, T>`.
+        //   - `RepeatedField` is a type alias for `NonNull`.
+        unsafe { &*(self as *const Self as *const RepeatedView<'a, T>) }
+    }
+}
+
+macro_rules! impl_repeated_primitives {
+    ($($t:ty),*) => {
+        $(
+            impl<'a> RepeatedView<'a, $t> {
+                pub fn len(&self) -> usize {
+                    self.inner.len()
+                }
+                pub fn is_empty(&self) -> bool {
+                    self.len() == 0
+                }
+                pub fn get(&self, index: usize) -> Option<$t> {
+                    self.inner.get(index)
+                }
+            }
+
+            impl<'a> RepeatedMut<'a, $t> {
+                pub fn push(&mut self, val: $t) {
+                    self.inner.push(val)
+                }
+                pub fn set(&mut self, index: usize, val: $t) {
+                    self.inner.set(index, val)
+                }
+            }
+
+            impl<'a> std::iter::Iterator for RepeatedFieldIter<'a, $t> {
+                type Item = $t;
+                fn next(&mut self) -> Option<Self::Item> {
+                    let val = self.inner.get(self.current_index);
+                    if val.is_some() {
+                        self.current_index += 1;
+                    }
+                    val
+                }
+            }
+
+            impl<'a> std::iter::IntoIterator for RepeatedView<'a, $t> {
+                type Item = $t;
+                type IntoIter = RepeatedFieldIter<'a, $t>;
+                fn into_iter(self) -> Self::IntoIter {
+                    RepeatedFieldIter { inner: self.inner, current_index: 0 }
+                }
+            }
+        )*
+    }
+}
+
+impl_repeated_primitives!(i32, u32, bool, f32, f64, i64, u64);
diff --git a/rust/shared.rs b/rust/shared.rs
index 3c4408d..f8a9d11 100644
--- a/rust/shared.rs
+++ b/rust/shared.rs
@@ -22,6 +22,7 @@
     pub use crate::proxied::{
         Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy,
     };
+    pub use crate::repeated::{RepeatedFieldRef, RepeatedMut, RepeatedView};
     pub use crate::string::{BytesMut, ProtoStr, ProtoStrMut};
 }
 pub use __public::*;
@@ -46,6 +47,7 @@
 mod optional;
 mod primitive;
 mod proxied;
+mod repeated;
 mod string;
 mod vtable;
 
diff --git a/rust/test/cpp/interop/test_utils.cc b/rust/test/cpp/interop/test_utils.cc
index d5c3784..5c27a95 100644
--- a/rust/test/cpp/interop/test_utils.cc
+++ b/rust/test/cpp/interop/test_utils.cc
@@ -8,7 +8,7 @@
 #include <cstddef>
 
 #include "absl/strings/string_view.h"
-#include "google/protobuf/rust/cpp_kernel/cpp_api.h"
+#include "rust/cpp_kernel/cpp_api.h"
 #include "google/protobuf/unittest.pb.h"
 
 extern "C" void MutateTestAllTypes(protobuf_unittest::TestAllTypes* msg) {
diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD
index e5be7b7..79d43dd 100644
--- a/rust/test/shared/BUILD
+++ b/rust/test/shared/BUILD
@@ -151,6 +151,9 @@
         "//rust:protobuf_cpp": "protobuf",
         "//rust/test/shared:matchers_cpp": "matchers",
     },
+    proc_macro_deps = [
+        "@crate_index//:paste",
+    ],
     tags = [
         # TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
         "not_build:arm",
@@ -170,6 +173,9 @@
         "//rust:protobuf_upb": "protobuf",
         "//rust/test/shared:matchers_upb": "matchers",
     },
+    proc_macro_deps = [
+        "@crate_index//:paste",
+    ],
     tags = [
         # TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
         "not_build:arm",
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 40098b1..910f9f1 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -9,6 +9,7 @@
 
 use googletest::prelude::*;
 use matchers::{is_set, is_unset};
+use paste::paste;
 use protobuf::Optional;
 use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_};
 
@@ -398,3 +399,51 @@
     // This should show it set to the OneofBytes but its not supported yet.
     assert_that!(msg.oneof_field(), matches_pattern!(not_set(_)));
 }
+
+macro_rules! generate_repeated_numeric_test {
+    ($(($t: ty, $field: ident)),*) => {
+        paste! { $(
+            #[test]
+            fn [< test_repeated_ $field _accessors >]() {
+                let mut msg = TestAllTypes::new();
+                assert_that!(msg.[< repeated_ $field >]().len(), eq(0));
+                assert_that!(msg.[<repeated_ $field >]().get(0), none());
+
+                let mut mutator = msg.[<repeated_ $field _mut >]();
+                mutator.push(1 as $t);
+                assert_that!(mutator.len(), eq(1));
+                assert_that!(mutator.get(0), some(eq(1 as $t)));
+                mutator.set(0, 2 as $t);
+                assert_that!(mutator.get(0), some(eq(2 as $t)));
+                mutator.push(1 as $t);
+
+                assert_that!(mutator.into_iter().collect::<Vec<_>>(), eq(vec![2 as $t, 1 as $t]));
+            }
+        )* }
+    };
+}
+
+generate_repeated_numeric_test!(
+    (i32, int32),
+    (u32, uint32),
+    (i64, int64),
+    (u64, uint64),
+    (f32, float),
+    (f64, double)
+);
+
+#[test]
+fn test_repeated_bool_accessors() {
+    let mut msg = TestAllTypes::new();
+    assert_that!(msg.repeated_bool().len(), eq(0));
+    assert_that!(msg.repeated_bool().get(0), none());
+
+    let mut mutator = msg.repeated_bool_mut();
+    mutator.push(true);
+    assert_that!(mutator.len(), eq(1));
+    assert_that!(mutator.get(0), some(eq(true)));
+    mutator.set(0, false);
+    assert_that!(mutator.get(0), some(eq(false)));
+    mutator.push(true);
+    assert_that!(mutator.into_iter().collect::<Vec<_>>(), eq(vec![false, true]));
+}
diff --git a/rust/upb.rs b/rust/upb.rs
index cd5cc77..57f9c55 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -7,7 +7,7 @@
 
 //! UPB FFI wrapper code for use by Rust Protobuf.
 
-use crate::__internal::{Private, RawArena, RawMessage};
+use crate::__internal::{Private, PtrAndLen, RawArena, RawMessage, RawRepeatedField};
 use std::alloc;
 use std::alloc::Layout;
 use std::cell::UnsafeCell;
@@ -284,6 +284,149 @@
     }
 }
 
+/// RepeatedFieldInner contains a `upb_Array*` as well as a reference to an
+/// `Arena`, most likely that of the containing `Message`. upb requires an Arena
+/// to perform mutations on a repeated field.
+#[derive(Clone, Copy, Debug)]
+pub struct RepeatedFieldInner<'msg> {
+    pub raw: RawRepeatedField,
+    pub arena: &'msg Arena,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct RepeatedField<'msg, T: ?Sized> {
+    inner: RepeatedFieldInner<'msg>,
+    _phantom: PhantomData<&'msg mut T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedField<'msg, T> {
+    pub fn len(&self) -> usize {
+        unsafe { upb_Array_Size(self.inner.raw) }
+    }
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+    pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+        Self { inner, _phantom: PhantomData }
+    }
+}
+
+// Transcribed from google3/third_party/upb/upb/message/value.h
+#[repr(C)]
+#[derive(Clone, Copy)]
+union upb_MessageValue {
+    bool_val: bool,
+    float_val: std::ffi::c_float,
+    double_val: std::ffi::c_double,
+    uint32_val: u32,
+    int32_val: i32,
+    uint64_val: u64,
+    int64_val: i64,
+    array_val: *const std::ffi::c_void,
+    map_val: *const std::ffi::c_void,
+    msg_val: *const std::ffi::c_void,
+    str_val: PtrAndLen,
+}
+
+// Transcribed from google3/third_party/upb/upb/base/descriptor_constants.h
+#[repr(C)]
+#[allow(dead_code)]
+enum UpbCType {
+    Bool = 1,
+    Float = 2,
+    Int32 = 3,
+    UInt32 = 4,
+    Enum = 5,
+    Message = 6,
+    Double = 7,
+    Int64 = 8,
+    UInt64 = 9,
+    String = 10,
+    Bytes = 11,
+}
+
+extern "C" {
+    #[allow(dead_code)]
+    fn upb_Array_New(a: RawArena, r#type: std::ffi::c_int) -> RawRepeatedField;
+    fn upb_Array_Size(arr: RawRepeatedField) -> usize;
+    fn upb_Array_Set(arr: RawRepeatedField, i: usize, val: upb_MessageValue);
+    fn upb_Array_Get(arr: RawRepeatedField, i: usize) -> upb_MessageValue;
+    fn upb_Array_Append(arr: RawRepeatedField, val: upb_MessageValue, arena: RawArena);
+}
+
+macro_rules! impl_repeated_primitives {
+    ($(($rs_type:ty, $union_field:ident, $upb_tag:expr)),*) => {
+        $(
+            impl<'msg> RepeatedField<'msg, $rs_type> {
+                #[allow(dead_code)]
+                fn new(arena: &'msg Arena) -> Self {
+                    Self {
+                        inner: RepeatedFieldInner {
+                            raw: unsafe { upb_Array_New(arena.raw, $upb_tag as std::ffi::c_int) },
+                            arena,
+                        },
+                        _phantom: PhantomData,
+                    }
+                }
+                pub fn push(&mut self, val: $rs_type) {
+                    unsafe { upb_Array_Append(
+                        self.inner.raw,
+                        upb_MessageValue { $union_field: val },
+                        self.inner.arena.raw(),
+                    ) }
+                }
+                pub fn get(&self, i: usize) -> Option<$rs_type> {
+                    if i >= self.len() {
+                        None
+                    } else {
+                        unsafe { Some(upb_Array_Get(self.inner.raw, i).$union_field) }
+                    }
+                }
+                pub fn set(&self, i: usize, val: $rs_type) {
+                    if i >= self.len() {
+                        return;
+                    }
+                    unsafe { upb_Array_Set(
+                        self.inner.raw,
+                        i,
+                        upb_MessageValue { $union_field: val },
+                    ) }
+                }
+            }
+        )*
+    }
+}
+
+impl_repeated_primitives!(
+    (bool, bool_val, UpbCType::Bool),
+    (f32, float_val, UpbCType::Float),
+    (f64, double_val, UpbCType::Double),
+    (i32, int32_val, UpbCType::Int32),
+    (u32, uint32_val, UpbCType::UInt32),
+    (i64, int64_val, UpbCType::Int64),
+    (u64, uint64_val, UpbCType::UInt64)
+);
+
+/// Returns a static thread-local empty RepeatedFieldInner for use in a
+/// RepeatedView.
+///
+/// # Safety
+/// TODO: Split RepeatedFieldInner into mut and const variants to
+/// enforce safety. The returned array must never be mutated.
+pub unsafe fn empty_array() -> RepeatedFieldInner<'static> {
+    // TODO: Consider creating empty array in C.
+    fn new_repeated_field_inner() -> RepeatedFieldInner<'static> {
+        let arena = Box::leak::<'static>(Box::new(Arena::new()));
+        // Provide `i32` as a placeholder type.
+        RepeatedField::<'static, i32>::new(arena).inner
+    }
+    thread_local! {
+        static REPEATED_FIELD: RepeatedFieldInner<'static> = new_repeated_field_inner();
+    }
+
+    REPEATED_FIELD.with(|inner| *inner)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -309,4 +452,35 @@
         };
         assert_eq!(&*serialized_data, b"Hello world");
     }
+
+    #[test]
+    fn i32_array() {
+        let mut arena = Arena::new();
+        let mut arr = RepeatedField::<i32>::new(&arena);
+        assert_eq!(arr.len(), 0);
+        arr.push(1);
+        assert_eq!(arr.get(0), Some(1));
+        assert_eq!(arr.len(), 1);
+        arr.set(0, 3);
+        assert_eq!(arr.get(0), Some(3));
+        for i in 0..2048 {
+            arr.push(i);
+            assert_eq!(arr.get(arr.len() - 1), Some(i));
+        }
+    }
+    #[test]
+    fn u32_array() {
+        let mut arena = Arena::new();
+        let mut arr = RepeatedField::<u32>::new(&mut arena);
+        assert_eq!(arr.len(), 0);
+        arr.push(1);
+        assert_eq!(arr.get(0), Some(1));
+        assert_eq!(arr.len(), 1);
+        arr.set(0, 3);
+        assert_eq!(arr.get(0), Some(3));
+        for i in 0..2048 {
+            arr.push(i);
+            assert_eq!(arr.get(arr.len() - 1), Some(i));
+        }
+    }
 }
diff --git a/rust/upb_kernel/BUILD b/rust/upb_kernel/BUILD
index dc65231..b06f182 100644
--- a/rust/upb_kernel/BUILD
+++ b/rust/upb_kernel/BUILD
@@ -8,6 +8,7 @@
         "//rust:__subpackages__",
     ],
     deps = [
+        "//upb:collections",
         "//upb:mem",
     ],
 )
diff --git a/rust/upb_kernel/upb_api.c b/rust/upb_kernel/upb_api.c
index 985749d..a30b4dd 100644
--- a/rust/upb_kernel/upb_api.c
+++ b/rust/upb_kernel/upb_api.c
@@ -8,4 +8,5 @@
 
 #define UPB_BUILD_API
 
-#include "upb/mem/arena.h" // IWYU pragma: keep
+#include "upb/collections/array.h"  // IWYU pragma: keep
+#include "upb/mem/arena.h"          // IWYU pragma: keep