pub struct OwnedView<V> { /* private fields */ }Expand description
An owned, 'static container for a decoded message view.
OwnedView holds a Bytes buffer alongside the decoded view, ensuring
the view’s borrows remain valid for the container’s lifetime. The inner
view is reached through reborrow(), which returns
it with a lifetime tied to &self.
This type is Send + Sync + 'static, making it suitable for use across
async boundaries, in tower services, and anywhere a 'static bound is
required.
§When to use
Use OwnedView when you need a zero-copy view that outlives the scope
where the buffer was received — for example, in an RPC handler where the
framework requires 'static types:
use buffa::view::OwnedView;
use bytes::Bytes;
let bytes: Bytes = receive_request_body().await;
let view = OwnedView::<PersonView>::decode(bytes)?;
// Field access through reborrow — the borrow is tied to `view`.
let person = view.reborrow();
println!("name: {}", person.name);
println!("id: {}", person.id);
// Convert to owned if you need to store or mutate
let owned: Person = view.to_owned_message();Generated code additionally provides a per-message FooOwnedView wrapper
around OwnedView<FooView<'static>> with per-field accessor methods
(owned.name(), owned.id(), …), so most handler code never touches
OwnedView or reborrow directly.
For scoped access where the buffer’s lifetime is known, use
MessageView::decode_view directly — it has zero overhead beyond the
decode itself.
§Why field access goes through reborrow
OwnedView stores V = FooView<'static>: the view’s borrows really point
into self’s Bytes buffer, and the 'static is a synthetic lifetime
established by the constructor. Exposing &V directly (for example via a
Deref impl) would let borrowed fields appear 'static to the
compiler and escape the OwnedView’s scope, dangling once it drops.
reborrow() narrows the synthetic 'static down
to the OwnedView’s real lifetime, so the borrow checker enforces the
actual validity of every field borrow:
// Inline reads: reborrow once, then use plain field access.
fn log(owned: &OwnedView<PersonView<'static>>) {
let person = owned.reborrow();
println!("{}", person.name);
}
// Returning a borrowed field: the result is tied to the OwnedView.
fn name<'a>(owned: &'a OwnedView<PersonView<'static>>) -> &'a str {
owned.reborrow().name // &'a str tied to the OwnedView's lifetime
}View fields are not reachable directly on the handle — this fails to
compile rather than handing out a 'static borrow into the buffer:
fn field(owned: &OwnedView<PersonView<'static>>) -> &'static str {
owned.name // error[E0609]: no field `name` on type `&OwnedView<...>`
}§Safety
Internally, OwnedView extends the view’s lifetime to 'static via
transmute in its constructors. This is sound because:
Bytesis reference-counted — its heap data pointer is stable across moves. The view’s borrows always point into valid memory.Bytesis immutable — the underlying data cannot be modified while borrowed.- A manual
Dropimpl explicitly drops the view before the bytes, ensuring no dangling references during cleanup. The view field usesManuallyDropto prevent the automatic drop from running out of order.
reborrow is a plain Rust subtype coercion (no
unsafe, no pointer cast): the ViewReborrow trait method coerces
&'b FooView<'static> into &'b FooView<'b> via standard lifetime
variance for covariant view types. See ViewReborrow’s docs for the
soundness argument.
Implementations§
Source§impl<V> OwnedView<V>where
V: MessageView<'static>,
impl<V> OwnedView<V>where
V: MessageView<'static>,
Sourcepub fn decode(bytes: Bytes) -> Result<Self, DecodeError>
pub fn decode(bytes: Bytes) -> Result<Self, DecodeError>
Decode a view from a Bytes buffer.
The view borrows directly from the buffer’s data. Because Bytes is
reference-counted and its data pointer is stable across moves, the
view’s borrows remain valid for the lifetime of this OwnedView.
§Errors
Returns DecodeError if the buffer contains invalid protobuf data.
Sourcepub fn decode_with_options(
bytes: Bytes,
opts: &DecodeOptions,
) -> Result<Self, DecodeError>
pub fn decode_with_options( bytes: Bytes, opts: &DecodeOptions, ) -> Result<Self, DecodeError>
Decode a view with custom DecodeOptions
(recursion limit, max message size).
§Errors
Returns DecodeError if the buffer is invalid or exceeds the
configured limits.
Sourcepub fn from_owned(msg: &V::Owned) -> Result<Self, DecodeError>
pub fn from_owned(msg: &V::Owned) -> Result<Self, DecodeError>
Create an OwnedView from an owned message by encoding then decoding.
This performs a full encode → decode round-trip: the message is serialized to protobuf bytes, then a zero-copy view is decoded from those bytes. This is useful when the original wire bytes are not available (e.g., after JSON deserialization or programmatic construction), but note the cost: one allocation + O(n) encode + O(n) decode.
For the common case where you already have wire bytes, prefer
decode instead.
§Errors
Returns DecodeError if the re-encoded bytes are somehow invalid
(should not happen for well-formed messages).
Sourcepub fn to_owned_message(&self) -> V::Owned
pub fn to_owned_message(&self) -> V::Owned
Convert the view to the corresponding owned message type.
bytes::Bytes-typed fields are produced via Bytes::slice_ref
into the retained buffer (zero-copy); other borrowed fields are
allocated and copied.
Sourcepub unsafe fn from_parts(bytes: Bytes, view: V) -> Self
pub unsafe fn from_parts(bytes: Bytes, view: V) -> Self
Create an OwnedView from a buffer and a pre-decoded view.
This avoids re-decoding when you already hold a decoded view and want
to wrap it for 'static use.
§Safety
The caller must ensure that all borrows in view point into the
data region of bytes. In practice, view must have been decoded
from bytes (or a sub-slice that bytes fully contains). Violating
this invariant causes undefined behavior (dangling references).
Sourcepub fn into_bytes(self) -> Bytes
pub fn into_bytes(self) -> Bytes
Consume the OwnedView, returning the underlying Bytes buffer.
The view is dropped before the buffer is returned.
Sourcepub fn reborrow<'b>(&'b self) -> &'b V::Reborrowed<'b>where
V: ViewReborrow,
pub fn reborrow<'b>(&'b self) -> &'b V::Reborrowed<'b>where
V: ViewReborrow,
Reborrow the view with a lifetime tied to &'b self.
OwnedView<V> stores V with a 'static lifetime — the actual borrows
point into self’s internal Bytes buffer and are only valid while
self is alive. reborrow makes that real lifetime visible to the borrow
checker: the returned &'b V::Reborrowed<'b> cannot outlive &'b self.
§Example
fn handler<'a>(req: &'a OwnedView<PersonView<'static>>) -> &'a str {
// The explicit annotation is for emphasis; inference works without it.
// If you need to name the lifetime, store the reborrow in a `let` first.
let req_view: &PersonView<'a> = req.reborrow();
req_view.name // zero-copy from the OwnedView's buffer
}The returned reference is tied to &'b self — the borrow checker
prevents the reborrowed view from outliving the OwnedView:
let name: &str;
{
// SAFETY: empty Bytes, no borrows — safe to construct directly.
let owned = unsafe {
OwnedView::<PersonView<'static>>::from_parts(
::buffa::bytes::Bytes::new(),
PersonView::default(),
)
};
name = owned.reborrow().name; // error[E0597]: `owned` does not live long enough
}
println!("{name}"); // name is dangling — borrow checker rejects this§How it works
The trait method ViewReborrow::reborrow is a plain Rust subtype
coercion: &'b V (where V = FooView<'static>) flows into the
return slot &'b V::Reborrowed<'b> (= &'b FooView<'b>). Variance
makes this safe — covariant view types narrow 'static down to
'b automatically. No unsafe, no pointer cast, no layout
assertions. OwnedView’s own invariant (every borrow in view
points into self.bytes, established by decode or upheld by the
unsafe from_parts caller) guarantees the pointed-to data lives
at least as long as 'b.
Trait Implementations§
impl<V> Eq for OwnedView<V>where
V: Eq,
Source§impl<V: Serialize> Serialize for OwnedView<V>
Available on crate feature json only.Serialize an OwnedView<V> by delegating to the inner view’s Serialize
impl.
impl<V: Serialize> Serialize for OwnedView<V>
json only.Serialize an OwnedView<V> by delegating to the inner view’s Serialize
impl.
Equivalent to serializing owned_view.reborrow() directly, so
serde_json::to_string(&owned_view) works on the handle itself. When
V is a buffa-generated view with generate_json enabled, this produces
protobuf JSON; the impl itself just forwards to whatever V::serialize
does.
Only available when the json feature is enabled.