Skip to main content

Serialized

Trait Serialized 

Source
pub trait Serialized {
    type Error: Error + Send + Sync + 'static;

    // Required method
    fn wire_bytes(
        &self,
        buf: &mut BytesMut,
    ) -> Result<WireBytes<'_>, Self::Error>;
}
Expand description

A value that owns its byte layout: Serialize means the framework’s codec does the work, Serialized means the type produces the bytes itself.

A Serialized value leaves the service byte-for-byte with no codec anywhere on the path, selected purely by the type, on every typed surface: the message(&value) entry of a publish builder (an Out slot, a transaction scope, any publisher), and the reply position (the same .reply() chain and reply("dest") clause an encoded reply uses).

The two shapes it covers are one method apart. A byte bag lends what it holds; a generated message (Protobuf, Cap’n Proto, a hand-rolled frame) holds fields, and writes them into the buffer the publish path hands it - so nothing is encoded twice and nothing intermediate is allocated.

§Implementing by hand

#[derive(Serialized)] (under the macros feature) covers a newtype or single-field struct over a byte buffer, and, with #[wire(encode = ..)], any type whose format names an encode function. Any other shape is the bytes plus the wire spellings that route the type onto the serialized wire: MessageWire for the typed publish entry, and ReplyShape for the reply position.

use std::convert::Infallible;

use ruststream::prelude::*;

struct Export(Vec<u8>);

impl Serialized for Export {
    type Error = Infallible;

    fn wire_bytes(&self, _buf: &mut BytesMut) -> Result<WireBytes<'_>, Infallible> {
        Ok(WireBytes::Own(&self.0))
    }
}

impl MessageWire for Export {
    type Wire = SerializedWire;
}

impl ReplyShape for Export {
    type Body = Self;
    type Headers = ();
    type Wire = SerializedReply;
}

let export = Export(vec![7, 9]);
let mut buf = BytesMut::new();
assert!(matches!(export.wire_bytes(&mut buf)?, WireBytes::Own(bytes) if bytes == [7, 9]));

A type that computes its bytes writes them into the buffer and lends that:

use std::convert::Infallible;

use ruststream::prelude::*;

struct Tick(u32);

impl Serialized for Tick {
    type Error = Infallible;

    fn wire_bytes(&self, buf: &mut BytesMut) -> Result<WireBytes<'_>, Infallible> {
        buf.extend_from_slice(&self.0.to_be_bytes());
        Ok(WireBytes::InBuffer)
    }
}

impl MessageWire for Tick {
    type Wire = SerializedWire;
}

let mut buf = BytesMut::new();
assert!(matches!(Tick(1).wire_bytes(&mut buf)?, WireBytes::InBuffer));
assert_eq!(&buf[..], &[0, 0, 0, 1]);

Required Associated Types§

Source

type Error: Error + Send + Sync + 'static

The failure of producing the bytes; Infallible for a value that already holds them.

Required Methods§

Source

fn wire_bytes(&self, buf: &mut BytesMut) -> Result<WireBytes<'_>, Self::Error>

The bytes the value publishes, exactly as they leave on the wire: the ones it already holds, answered as WireBytes::Own, or the ones it writes into buf, answered as WireBytes::InBuffer.

One method rather than a lend-or-compute pair, so a value cannot answer the two differently, and a value that carries its bytes never touches buf - which is why the publish path can hand it a buffer that has not allocated. Which answer it gives is what the path above it needs to know: a transport that keeps the payload is handed the buffer itself where the value wrote into it, and copies only where the bytes are the value’s own.

§Errors

Returns Self::Error when the value’s own encoder rejects it. A value that already holds its bytes cannot fail, and says so with Infallible.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§