capnproto-rust
capnproto-rust copied to clipboard
How to return a reader?
I want to store a Reader inside of a struct but it only uses a reference a does not implement Clone.
use super::capnp_error::CapnpError;
use capnp::{message::ReaderOptions, serialize_packed};
use std::io::Cursor;
pub struct ReaderWrapper<'a> {
reader: &'a Reader<'a>,
}
impl<'a> TryFrom<Vec<u8>> for ReaderWrapper<'a> {
type Error = CapnpError;
fn try_from(buffer: Vec<u8>) -> Result<ReaderWrapper<'a>, CapnpError> {
let reader =
&serialize_packed::read_message(&mut Cursor::new(buffer), ReaderOptions::new())?;
let typed_reader = reader.get_root::<Reader>()?;
Ok(ReaderWrapper {
reader: &typed_reader,
})
}
}
cannot return value referencing temporary value returns a value referencing data owned by the current function
Okay, so my understanding is that you're trying to keep the original Vec<u8> around, and also create a ReaderWrapper which reads directly from the Vec<u8> without modifying it or storing an intermediate representation.
If that's correct, there are a few issues with your initial attempt:
- You can't
impl TryFrom<Vec<u8>>, because that will consume theVec<u8>that you're trying to reference. You canimpl TryFrom<&'a [u8]>, which has two advantages:- doesn't consume the buffer
- properly relates
'ato the buffer's lifetime.
- You're using
serialize_packed::read_message, which can't work. The packed representation has to be un-packed before it can be read, so it's only possible to read directly from theVec<u8>without copying if it's in the standard binary representation to begin with. In this case, you can usecapnp::serialize::read_message_from_flat_sliceto read directly from a slice.
Here's my first attempt at something like what you're asking (full repo), based on the Point example in the capn-proto-rust repo readme:
use anyhow::Result;
use capnp::{message::ReaderOptions, serialize::SliceSegments};
use points_capnp::point;
pub struct ReaderWrapper<'a> {
reader: capnp::message::Reader<SliceSegments<'a>>,
}
impl<'a> TryFrom<&'a [u8]> for ReaderWrapper<'a> {
type Error = anyhow::Error;
fn try_from(mut buffer: &'a [u8]) -> Result<ReaderWrapper<'a>> {
let reader =
capnp::serialize::read_message_from_flat_slice(&mut buffer, ReaderOptions::new())?;
Ok(ReaderWrapper { reader })
}
}
impl<'a> ReaderWrapper<'a> {
fn print_point(&self) -> Result<()> {
let point_reader = self.reader.get_root::<point::Reader>()?;
println!("x = {:.2}", point_reader.get_x());
println!("y = {:.2}", point_reader.get_y());
Ok(())
}
}
Note that your ReaderWrapper stores the "typed" reader (would be points_capnp::point::Reader in my example), whereas mine just stores the "untyped" capnp::message::Reader, which is suboptimal because as you can see in the print_point function, reading from the reader is unnecessarily fallible (the failure should happen once when the ReaderWrapper is created, not every time it's read from).
I thought this would be a good starting point - I haven't tried storing the "typed" version yet, although I think it might be possible with something like the owning_ref crate, although I'm not 100% sure.