tree: 230c463fef33baf6c5ca5e9b523a62b261b275bc [path history] [tgz]
  1. example/
  2. heapsize/
  3. heapsize_derive/
  4. Cargo.toml
  5. README.md
examples/heapsize/README.md

A complete working implementation of a custom derive. Written in Rust 2018 style but otherwise works on any Rust compiler 1.15+.

We are deriving the HeapSize trait which computes an estimate of the amount of heap memory owned by a value.

pub trait HeapSize {
    /// Total number of bytes of heap memory owned by `self`.
    fn heap_size_of_children(&self) -> usize;
}

The custom derive allows users to write #[derive(HeapSize)] on data structures in their program.

#[derive(HeapSize)]
struct Demo<'a, T: ?Sized> {
    a: Box<T>,
    b: u8,
    c: &'a str,
    d: String,
}

The trait impl generated by the custom derive here would look like:

impl<'a, T: ?Sized + heapsize::HeapSize> heapsize::HeapSize for Demo<'a, T> {
    fn heap_size_of_children(&self) -> usize {
        0 + heapsize::HeapSize::heap_size_of_children(&self.a)
            + heapsize::HeapSize::heap_size_of_children(&self.b)
            + heapsize::HeapSize::heap_size_of_children(&self.c)
            + heapsize::HeapSize::heap_size_of_children(&self.d)
    }
}

The implementation of heapsize_derive demonstrates some attention to “spans” of error messages. For each subexpression in the generated code we apply the span of the input fragment under which we would want to trigger a compiler error if the subexpression fails to compile. In this example, each recursive call to heap_size_of_children is associated with the span of the corresponding struct field. Thus we get errors in the right place if any of the field types do not implement the HeapSize trait.

error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
 --> src/main.rs:7:5
  |
7 |     bad: std::thread::Thread,
  |     ^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`

Some unstable APIs in the proc-macro2 crate let us improve this further by joining together the span of the field name and the field type. There is no difference in our code -- everything is as shown in this directory -- but building the example crate with cargo build shows errors like the one above and building with RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build is able to show errors like the following.

error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
 --> src/main.rs:7:5
  |
7 |     bad: std::thread::Thread,
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`