diff --git a/rust/radixbuf/src/serialize.rs b/rust/radixbuf/src/serialize.rs
--- a/rust/radixbuf/src/serialize.rs
+++ b/rust/radixbuf/src/serialize.rs
@@ -110,6 +110,28 @@
     }
 }
 
+#[derive(Debug, PartialEq, Clone)]
+pub struct LinkedNode<T>(T, Offset<LinkedNode<T>>);
+
+impl<T> LinkedNode<T> {
+    pub fn new(value: T, offset: Offset<LinkedNode<T>>) -> Self { LinkedNode(value, offset) }
+}
+
+impl<T: Serialize> Serialize for LinkedNode<T> {
+    fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
+        self.0.write_to(w)?;
+        self.1.write_to(w)
+    }
+
+    fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
+        let value: T = T::read_from(r)?;
+        let offset = Offset::read_from(r)?;
+        Ok(LinkedNode::new(value, offset))
+    }
+}
+
+impl<T: FixedSizedSerialize> FixedSizedSerialize for LinkedNode<T> {}
+
 macro_rules! impl_serialize_unsigned {
     ($T: ty, $S: expr) => {
         impl Serialize for $T {
@@ -182,6 +204,15 @@
         fn shrink(&self) -> Box<Iterator<Item = Self>> { unimplemented!() }
     }
 
+    impl<T: Arbitrary> Arbitrary for LinkedNode<T> {
+        fn arbitrary<G: Gen>(gen: &mut G) -> Self {
+            LinkedNode::new(T::arbitrary(gen), unsafe {
+                Offset::from_raw(u64::arbitrary(gen))
+            })
+        }
+        fn shrink(&self) -> Box<Iterator<Item = Self>> { unimplemented!() }
+    }
+
     fn check_round_trip<T: Serialize + Clone + PartialEq + Debug>(keys: Vec<T>) -> bool {
         let mut buf = Buffer(Rc::new(RefCell::new(Cursor::new(vec![]))));
         let mut offsets: Vec<Offset<T>> = vec![];
@@ -221,6 +252,14 @@
         fn test_round_trip_u64(v: Vec<u64>) -> bool {
             check_round_trip(v)
         }
+
+        fn test_round_trip_linkednode_variantbytes(v: Vec<LinkedNode<VariantBytes>>) -> bool {
+            check_round_trip(v)
+        }
+
+        fn test_round_trip_linkednode_u32(v: Vec<LinkedNode<u32>>) -> bool {
+            check_round_trip(v)
+        }
     }
 
     #[test]