#rkyv #macro-derive #serialization #remote #with

rkyv-with

为rkyv的*With特性提供第三方派生宏。

3个版本

0.1.2 2023年9月25日
0.1.1 2023年5月29日
0.1.0 2023年3月30日

#344进程宏

每月下载量37

MIT 许可证

63KB
1K SLoC

rkyv-with

为rkyv的{Archive/Serialize/Deserialize}With特性提供第三方派生宏。

此派生宏的主要用途是能够在不实现自身ArchiveSerializeDeserialize的远程类型上使用rkyv。这为rusts孤儿规则提供了一个类似的解决方案,类似于serde的远程支持。

ArchiveWith派生宏实现了ArchiveWithSerializeWith特性。对于DeserializeWith特性,请使用DeserializeWith派生宏。

#[archive_with(...)]属性有助于微调实现。

  • archive_with(from(TypeName))表示原始类型是什么。此属性需要在类型定义的最高级别指定。允许使用多个逗号分隔的类型,即from(Type1, Type2)。此属性也可以用于字段。
  • archive_with(via(TypeWrapper))提供了一种将字段类型转换为其他类型的方法,例如,不可存档的类型包含一个PathBuf字段,而在可存档的对应字段中它是一个String,通过指定via(rkyv::with::AsString)
  • archive_with(getter = "path::to::function")必须在使用包含私有字段的不可存档类型时使用。该函数必须是Fn(&U) -> TFn(&U) -> &T,其中U是不可存档的类型,而T是字段的类型。
  • archive_with(getter_owned)可以在指定getter = "..."的同时使用,当函数接受一个所有者实例而不是引用时。

应用宏

// Both trait and macro must be imported
use rkyv::with::ArchiveWith;
use rkyv_with::ArchiveWith;

// This could come from some dependency or otherwise remote module.
// Importantly, this does not implement the rkyv traits
struct UnarchivableInner {
    a: u32,
    b: Vec<u8>,
    unimportant: String,
}

#[derive(rkyv::Archive, ArchiveWith)]
#[archive_with(from(UnarchivableInner))] // must be specified
struct ArchivableInner {
    // fields must have the same name
    a: u32,
    #[with(rkyv::with::CopyOptimize)] // archive wrappers work as usual
    b: Vec<u8>,
    // not all fields must be included but if fields
    // are omitted, `DeserializeWith` can not be derived
}

struct UnarchivableOuter {
    archivable_field: i32,
    buf: std::path::PathBuf,
    inner: UnarchivableInner,
    opt: Option<UnarchivableInner>,
    vec: Vec<UnarchivableInner>,
}

#[derive(rkyv::Archive, ArchiveWith)]
#[archive_with(from(UnarchivableOuter))]
struct ArchivableOuter {
    // If the field's type is archivable, no annotation is required
    archivable_field: i32,
    // Otherwise one must specify the original type through `from(...)`
    // as well as a type to convert between the types through `via(...)`
    #[archive_with(from(std::path::PathBuf), via(rkyv::with::AsString))]
    buf: String,
    // If the archivable type itself serves as converter, `via(...)` can be omitted
    #[archive_with(from(UnarchivableInner))]
    inner: ArchivableInner,
    // As such, e.g. for `Vec` and `Option` it's necessary to specify the full
    // original type, as well as the archivable type wrapped in rkyv's Map wrapper
    #[archive_with(
        from(Option<UnarchivableInner>),
        via(rkyv::with::Map<ArchivableInner>),
    )]
    opt: Option<ArchivableInner>,
    // Using the unarchivable type in combination with a with-wrapper is
    // of course also always doable
    #[with(rkyv::with::Map<ArchivableInner>)]
    vec: Vec<UnarchivableInner>,
}

使用生成的实现

use rkyv::with::{ArchiveWith, DeserializeWith, With};
use rkyv::{Archive, Deserialize, Infallible, Serialize};
use rkyv_with::{ArchiveWith, DeserializeWith};

struct Unarchivable {
    a: String,
}

// Can be serialized as usual and
// also serves as serialization wrapper
#[derive(Archive, ArchiveWith, Deserialize, DeserializeWith)]
#[archive_with(from(Unarchivable))]
struct ArchivesTheUnarchivable {
    a: String,
}

// Can be serialized as usual
#[derive(Archive, Serialize)]
struct Container {
    a: i32,
    #[with(ArchivesTheUnarchivable)]
    b: Unarchivable,
}

let unarchivable = Unarchivable { a: String::new() };

// Serialize the instance
let wrapper = With::<_, ArchivesTheUnarchivable>::cast(&unarchivable);
let bytes = rkyv::to_bytes::<_, 32>(wrapper).unwrap();

let archived = unsafe { rkyv::archived_root::<ArchivesTheUnarchivable>(&bytes) };

// Can go back to the original type
let deserialized_unarchivable: Unarchivable =
    ArchivesTheUnarchivable::deserialize_with(archived, &mut Infallible).unwrap();

// Or stick with the wrapper
let deserialized_wrapper: ArchivesTheUnarchivable =
    archived.deserialize(&mut Infallible).unwrap();

私有字段

如果字段因为它们是私有的而不能直接访问,那么派生特质需要手动指定获取器函数。

use rkyv::with::ArchiveWith;
use rkyv::Archive;
use rkyv_with::ArchiveWith;

// Imagine again that this is a remote module that you have no way of modifying
mod remote {
    #[derive(Clone)]
    pub struct Remote {
        pub public_field: u32,
        private_field: u32,
    }

    impl Remote {
        // By default it will be assumed that the function will take a reference
        pub fn to_private_field(&self) -> u32 {
            self.private_field
        }

        // If it takes ownership instead, `getter_owned` will need to be added as seen below
        pub fn into_private_field(self) -> u32 {
            self.private_field
        }

        // A publicly available way of creating the type in case you need to deserialize
        pub fn new(public_field: u32, private_field: u32) -> Self {
            Self { public_field, private_field }
        }
    }
}

#[derive(Archive, ArchiveWith)]
#[archive_with(from(remote::Remote))]
struct NativeByReference {
    public_field: u32,
    // Specifying the path to the getter function that takes a reference
    #[archive_with(getter = "remote::Remote::to_private_field")]
    private_field: u32,
}

#[derive(Archive, ArchiveWith)]
#[archive_with(from(remote::Remote))]
struct NativeByValue {
    public_field: u32,
    // If the function takes ownership, be sure to also specify `getter_owned`
    #[archive_with(getter = "remote::Remote::into_private_field", getter_owned)]
    private_field: u32,
}

// Since creating instances of a type that has private fields cannot be done in a general way,
// `DeserializeWith` cannot be derived for such types and instead has to be implemented manually.
// An implementation for the example above could look as follows:

use rkyv::with::DeserializeWith;
use rkyv::{Archived, Fallible};

impl<D: Fallible> DeserializeWith<Archived<NativeByValue>, remote::Remote, D> for NativeByValue {
    fn deserialize_with(
        archived: &Archived<NativeByValue>,
        _deserializer: &mut D,
    ) -> Result<remote::Remote, <D as Fallible>::Error> {
        // Use whichever method is available to create an instance
        Ok(remote::Remote::new(archived.public_field, archived.private_field))
    }
}

依赖关系

~280–730KB
~18K SLoC