[][src]Derive Macro dispose_derive::Dispose

#[derive(Dispose)]
{
    // Attributes available to this derive:
    #[dispose]
}

Add trivial Dispose support to a struct or enum where the contained values implement Dispose or DisposeWith<W>.

This macro is designed to reduce the boilerplate for writing custom containers housing Dispose or DisposeWith resources.

The #[dispose] attribute

Examples

Here's a dead-simple example:

use dispose::{prelude::*, Disposable};

struct MyResource {
    important_stuff: String,
}

impl Dispose for MyResource {
    fn dispose(self) {
        println!("disposing {:?}", self.important_stuff);
    }
}

struct MyOtherResource {
    handle: u32,
}

impl Dispose for MyOtherResource {
    fn dispose(self) {
        println!("releasing handle {}", self.handle)
    }
}

// The derive macro makes it trivial to implement Dispose on a container type for these
// resources.
#[derive(Dispose)]
struct MyContainer {
    res: MyResource,
    other: MyOtherResource,
}

impl MyContainer {
    fn new(important_stuff: impl Into<String>, handle: u32) -> Disposable<Self> {
        let important_stuff = important_stuff.into();

        Self {
            res: MyResource { important_stuff },
            other: MyOtherResource { handle },
        }.into()
    }
}

{
    let container = MyContainer::new("foobar", 27);

    // Do some stuff with container here...
}
// This prints:
// disposing "foobar"
// releasing handle 27

Here's a more real-world example, using the gfx-hal crate:

use gfx_hal::{prelude::*, Backend, device::Device};

// First, some setup - since this is non-trivial, the macro can't help here.
struct Buffer<B: Backend>(B::Buffer);
struct Memory<B: Backend>(B::Memory);

impl<B: Backend> DisposeWith<&B::Device> for Buffer<B> {
    fn dispose_with(self, dev: &B::Device) { unsafe { dev.destroy_buffer(self.0) } }
}

impl<B: Backend> DisposeWith<&B::Device> for Memory<B> {
    fn dispose_with(self, dev: &B::Device) { unsafe { dev.free_memory(self.0) } }
}

/// A single buffer with its own device memory allocation.
#[derive(Dispose)]
struct SingleBuffer<'a, B: Backend> {
    #[dispose(ignore)]
    dev: &'a B::Device,
    #[dispose(with = .dev)]
    buf: Buffer<B>,
    #[dispose(with = .dev)]
    mem: Memory<B>,
}

impl<'a, B: Backend> SingleBuffer<'a, B> {
    fn new(dev: &'a B::Device, buf: Buffer<B>, mem: Memory<B>) -> Disposable<Self> {
        Self { dev, buf, mem }.into()
    }
}

/// A set of buffers sharing a single memory allocation.
#[derive(Dispose)]
struct MultiBuffer<'a, B: Backend> {
    #[dispose(ignore)]
    dev: &'a B::Device,
    #[dispose(with = .dev)]
    bufs: Vec<Buffer<B>>,
    #[dispose(with = .dev)]
    mem: Memory<B>,
}

impl<'a, B: Backend> MultiBuffer<'a, B> {
    fn new(
        dev: &'a B::Device,
        bufs: impl IntoIterator<Item = Buffer<B>>,
        mem: Memory<B>,
    ) -> Disposable<Self> {
        Self {
            dev,
            bufs: bufs.into_iter().collect(),
            mem,
        }.into()
    }
}

// Acquire a device here.

// Now we can create and manage a container for a single buffer...
let buf = SingleBuffer::new(
    a_device,
    create_buffer(a_device),
    alloc_memory(a_device),
);

// ...or multiple buffers, with allocation sharing.
// And, there's no excessive copies of a_device stored in memory!
let bufs = MultiBuffer::new(
    a_device,
    (0..16).into_iter().map(|_| create_buffer(a_device)),
    alloc_memory(a_device),
);

// Draw cool things with the buffers here...