Crate dstify

Crate dstify 

Source
Expand description

§DSTify

This crate enables safe construction of custom dynamically-sized types (DSTs).

Consists of a derive procedural macro that can be applied on any #[repr(C)] struct with dynamically-sized last field. Structs with both named and unnamed fields (tuple structs) are supported. The last field may be either a slice DST or dyn Trait DST.

§slice DST example

use dstify::Dstify;

#[derive(Dstify, Debug)]
#[repr(C)]
struct FileWithPath {
    file: File,
    path: Path, // `slice` DST
}

impl FileWithPath {
    pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Box<Self>> {
        let path = path.as_ref();
        let file = File::open(path)?;
        Ok(FileWithPath::init_unsized(file, path))
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn file(&self) -> &File {
        &self.file
    }
}

let named_file = FileWithPath::open("Cargo.toml")?;
println!("{named_file:?}");
assert_eq!(size_of_val(&named_file), 16);

§dyn Trait DST example

use dstify::Dstify;
use std::{fmt::Debug, io, sync::Arc};

#[derive(Dstify, Debug)]
#[repr(C)]
struct DbgExtra {
    line: u64,
    column: u64,
    dbg: dyn Debug, // `dyn Trait` DST
}

impl DbgExtra {
    pub fn new<D: Debug>(line: u64, col: u64, dbg: D) -> Arc<Self> {
        // call the method generated by `Dstify` proc macro
        Self::init_unsized(line, col, dbg)
    }
}
let dbg = DbgExtra::new(10, 27, io::Error::new(io::ErrorKind::Interrupted, ":/"));
println!("{dbg:#?}");

The Dstify proc macro in above example generates an impl block for the FileWithPath struct with two “static” methods, init_unsized and init_unsized_checked. Both of them accept all the struct fields as arguments in definition order. The type of the last one, being a DST, becomes:

  • for slice DST: a reference
  • for dyn Trait DST: an owned value

The return type R, determines the smart pointer type that should be constructed. The bounding trait - SmartPointer, is implemented for Box, Rc and Arc.

The checked method returns LayoutError if the size of the resulting instance would exceed isize::MAX bytes. The “unchecked” method panics in that case.

impl FileWithPath {
    fn init_unsized<R>(file: File, path: &Path) -> R
    where
        R: dstify::SmartPointer<Self>
    {
        // ...
    }
    fn init_unsized_checked<R>(file: File, path: &Path) -> Result<R, LayoutError>
    where
        R: dstify::SmartPointer<Self>
    {
        // ...
    }
}

§Requirements

The type must be a struct. enums and unions are not supported as it’s forbidden to define a dynamically-sized enum or union in current rust. It must be annotated with #[repr(C)] and the last field must be a DST.

#[derive(Dstify)]
#[repr(C)]
struct Fail2Compile<'a> {
    not_dst: &'a [u64], // fails to compile due to last field not being a DST
}
// fails to compile due to missing `#[repr(C)]`
#[derive(Dstify)]
struct Fail2Compile {
    dst: [u128],
}

§Features

  • “std” - enabled by default
    removing this feature (using default-features = false) enables !#[no_std] support.

Traits§

SmartPointer
Internal trait implemented for smart pointer types that init_unsized and init_unsized_checked can return.

Derive Macros§

Dstify