Open Fuuzetsu opened 7 months ago
@Fuuzetsu Does serde
support it? I feel it is a rare case and users may just implement an internal helper struct for serializing/deserializing (kind of like this)
@Fuuzetsu Does
serde
support it? I feel it is a rare case and users may just implement an internal helper struct for serializing/deserializing (kind of like this)
Seems to work out of the box in serde for this example: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2005b735ca5ca80559abf53f03b36475
serde
achieves #[repr(packed)]
partially working by surrounding (all) referenced fields with brackets { ... }
, which somewhat implicitly copies the field's value.
(it would be borsh::BorshSerialize::serialize(&{self.0}, writer)?;
in borsh
-s case
It works fine for Copy
fields:
#[derive(serde::Serialize)]
#[repr(packed)]
struct Bar {
x: u8,
y: u64,
}
and is a compilation error for non-Copy
types:
#[derive(serde::Serialize)]
#[repr(packed)]
struct Foo {
x: RefCell<u8>,
y: u64,
}
1 error[E0507]: cannot move out of `self.x` which is behind a shared reference
--> src/main.rs:12:10
|
12 | #[derive(serde::Serialize)]
| ^^^^^^^^^^^^^^^^ move occurs because `self.x` has type `RefCell<u8>`, which does not implement the `Copy` trait
|
= note: `#[derive(serde::Serialize)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
= note: this error originates in the derive macro `serde::Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)
imo replicating serde
-s behaviour with packed
structs is worse than letting a user transparently work with unaligned references.
a more versatile template for derives, allowing to work with non-Copy
types, might look like the following:
use core::mem;
use core::ops::Deref;
use core::ptr;
use std::rc::Rc;
#[repr(packed)]
struct Foo {
x: Rc<u8>,
y: u64,
}
impl borsh::ser::BorshSerialize for Foo {
fn serialize<W: borsh::io::Write>(
&self,
writer: &mut W,
) -> ::core::result::Result<(), borsh::io::Error> {
borsh::BorshSerialize::serialize(
mem::ManuallyDrop::new(unsafe { ptr::read_unaligned(ptr::addr_of!(self.x)) }).deref(),
writer,
)?;
borsh::BorshSerialize::serialize(
mem::ManuallyDrop::new(unsafe { ptr::read_unaligned(ptr::addr_of!(self.y)) }).deref(),
writer,
)?;
Ok(())
}
}
fn main() {
let foo = Foo {
x: Rc::new(42),
y: 182,
};
println!("{:?}", borsh::to_vec(&foo).unwrap());
}
src/main.rs
:Cargo.toml
:Trying to build this fails with:
The macro itself expands to:
The work-around is to write a manual implementation that copies the underlying value first and uses a reference to that, like this:
I'm not sure what the best way forward/workaround is, but I thought I'd open a ticket anyway.