Repository: facet-rs/facet (Rust)

Issue:
facet-xdr Optional Serialization missing leading bytes
In XDR, a `Some` optional should be serialized with `\0\0\0\1`, followed by the data. Facet handles this while decoding ([facet-xdr/src/lib.rs#L491](https://github.com/facet-rs/facet/blob/1ae8c979feca4ec655fa6e0f8e9e4cf4d2180072/facet-xdr/src/lib.rs#L491)), however when serializing the leading bytes are omitted.

The easiest test case is that [the spec](https://datatracker.ietf.org/doc/html/rfc1832#section-3.19) says that `type-name *identifier;` (`Optional<T>`) is equivalent to `type-name identifier<1>;` (`Vec<T>`), but facet serializes them differently.

```rust
#[derive(Debug, Clone, Copy, facet::Facet)]
struct Foo {
    elem: u32,
}

fn main() {
    let inner = Foo { elem: 57 };

    let option_serialization = facet_xdr::to_vec(&Some(inner)).unwrap();
    let vec_serialization = facet_xdr::to_vec(&vec![inner]).unwrap();
    assert_eq!(
        option_serialization, vec_serialization,
        "Option and Vec Serialization are not identical.",
    );
}

```
```
thread 'main' panicked at src/main.rs:11:5:
assertion `left == right` failed: Option and Vec Serialization are not identical.
  left: [0, 0, 0, 57]
 right: [0, 0, 0, 1, 0, 0, 0, 57]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
****
```


Discussion:
Thanks - I obviously picked this up when writing the deserializer but not the serializer



Patch:
diff --git a/facet-serialize/CHANGELOG.md b/facet-serialize/CHANGELOG.md
index f35c83cf3..d2c016f9b 100644
--- a/facet-serialize/CHANGELOG.md
+++ b/facet-serialize/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+- Add start_some to serializer trait
+
 ## [0.24.18](https://github.com/facet-rs/facet/compare/facet-serialize-v0.24.17...facet-serialize-v0.24.18) - 2025-07-03
 
 ### Other
diff --git a/facet-serialize/src/lib.rs b/facet-serialize/src/lib.rs
index 5f3393ed5..c69b20143 100644
--- a/facet-serialize/src/lib.rs
+++ b/facet-serialize/src/lib.rs
@@ -61,6 +61,11 @@ pub trait Serializer<'shape> {
     /// Serialize a `None` variant of an Option type.
     fn serialize_none(&mut self) -> Result<(), Self::Error>;
 
+    /// Serialize a `Some` discriminant of an Option type.
+    fn start_some(&mut self) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
     /// Serialize a unit value `()`.
     fn serialize_unit(&mut self) -> Result<(), Self::Error>;
 
@@ -461,6 +466,7 @@ where
                     (Def::Option(_), _) => {
                         let opt = cpeek.into_option().unwrap();
                         if let Some(inner_peek) = opt.value() {
+                            serializer.start_some()?;
                             stack.push(SerializeTask::Value(inner_peek, None));
                         } else {
                             serializer.serialize_none()?;
diff --git a/facet-xdr/CHANGELOG.md b/facet-xdr/CHANGELOG.md
index 003454cfe..b76d4fa16 100644
--- a/facet-xdr/CHANGELOG.md
+++ b/facet-xdr/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+- fix serialization for Option<T>
+
 ## [0.1.19](https://github.com/facet-rs/facet/compare/facet-xdr-v0.1.18...facet-xdr-v0.1.19) - 2025-07-03
 
 ### Other
diff --git a/facet-xdr/src/lib.rs b/facet-xdr/src/lib.rs
index 74baef422..c4f4efac5 100644
--- a/facet-xdr/src/lib.rs
+++ b/facet-xdr/src/lib.rs
@@ -138,8 +138,16 @@ impl<'shape, W: Write> Serializer<'shape> for XdrSerializer<'_, W> {
         Ok(())
     }
 
+    fn start_some(&mut self) -> Result<(), Self::Error> {
+        self.writer
+            .write_all(&1u32.to_be_bytes())
+            .map_err(Self::Error::Io)
+    }
+
     fn serialize_none(&mut self) -> Result<(), Self::Error> {
-        Ok(())
+        self.writer
+            .write_all(&0u32.to_be_bytes())
+            .map_err(Self::Error::Io)
     }
 
     fn serialize_unit(&mut self) -> Result<(), Self::Error> {


Test patch:
diff --git a/facet-xdr/tests/mod.rs b/facet-xdr/tests/mod.rs
index 49e3e7fbf..be343aa55 100644
--- a/facet-xdr/tests/mod.rs
+++ b/facet-xdr/tests/mod.rs
@@ -47,3 +47,20 @@ fn test_deserialize_file_example() {
     let file: File = deserialize(&FILE_EXAMPLE_BYTES)?;
     assert_eq!(file, file_example());
 }
+
+#[derive(Debug, Clone, Copy, Facet, PartialEq)]
+struct Data {
+    elem: u32,
+}
+
+#[test]
+fn test_option_is_the_same_as_vec() {
+    let inner = Data { elem: 57 };
+
+    let option_serialization = facet_xdr::to_vec(&Some(inner)).unwrap();
+    let vec_serialization = facet_xdr::to_vec(&vec![inner]).unwrap();
+    assert_eq!(
+        option_serialization, vec_serialization,
+        "Option and Vec Serialization are not identical.",
+    );
+}