Skip to content

Implement #[bitcode(with = LocalType)] RemoteType #115

Description

@caibear

#47 has not yet been implemented due to complexity surrounding:

  1. serde::Serialize::serialize can return errors, but bitcode::encode can't, we would have to panic.
  2. serde::Deserialize::deserialize can return errors, but luckily bitcode::decode can as well. Unfortunately, we would have to implement infrastructure for dropping T's owned by bitcode::Decoders while keeping the decoders themselves alive. Keeping unsafe code involving drop panic-safe is a non-trivial task.

Therefore, a simpler alternative to get most bitcode users unstuck would be something like

use bespoke_string::CompactZeroCopyArcStr;

#[derive(bitcode::Encode, bitcode::Decode)]
struct User {
    #[bitcode(with = LocalString)]
    first_name: CompactZeroCopyArcStr,
    // If you wanted a different encoder and decoder type (e.g. non &str types cannot borrow during decode).
    #[bitcode(encode_with = LocalString, decode_with = LocalString)]
    last_name: CompactZeroCopyArcStr,
}

#[derive(bitcode::Encode, bitcode::Decode)]
struct LocalString<'a>(&'a str);

impl<'a> From<&'a CompactZeroCopyArcStr> for LocalString<'a> {
    fn from(v: &'a CompactZeroCopyArcStr) -> Self {
        Self(v.as_str())
    }
}

impl From<LocalString<'_>> for CompactZeroCopyArcStr {
    fn from(v: LocalString) -> Self {
        Self::new(v.0)
    }
}

See also:
#13
https://docs.rs/serde_with
https://rkyv.org/derive-macro-features/remote-derive.html

To avoid the complexity present with #47, you would not be able to return errors during decoding.
If such an error did occur, you would have to construct a zero value of the type. Some examples:

  • Time outside range, return the zero time
  • FiniteVec has too many elements, return an empty vec
  • OsString/CString has invalid characters, return an empty string

If for some reason you needed to know if an error occurred (not just handling decoding untrusted bytes from the network), you could store the error out of band in a thread_local and check for errors after decode.

Important: Panicking during decode and using catch_unwind is not a valid implementation of error handling. Bitcode relies on in place initialization during decode for performance, so any panics during decode result in leaked memory.
Currently the only way to observe this with the public API is to panic in PartialEq, Ord, or Hash inside the HashMap and BTreeMap decoders.
Because this feature would present a larger surface for accidently leaking memory, it's probably best for bitcode to catch_unwind (in at least debug mode) and cause the process to abort with an error message explaining the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions