aboutsummaryrefslogtreecommitdiff
path: root/syn/examples/heapsize
diff options
context:
space:
mode:
Diffstat (limited to 'syn/examples/heapsize')
-rw-r--r--syn/examples/heapsize/Cargo.toml2
-rw-r--r--syn/examples/heapsize/README.md72
-rw-r--r--syn/examples/heapsize/example/Cargo.toml9
-rw-r--r--syn/examples/heapsize/example/src/main.rs28
-rw-r--r--syn/examples/heapsize/heapsize/Cargo.toml9
-rw-r--r--syn/examples/heapsize/heapsize/src/lib.rs64
-rw-r--r--syn/examples/heapsize/heapsize_derive/Cargo.toml14
-rw-r--r--syn/examples/heapsize/heapsize_derive/src/lib.rs96
8 files changed, 294 insertions, 0 deletions
diff --git a/syn/examples/heapsize/Cargo.toml b/syn/examples/heapsize/Cargo.toml
new file mode 100644
index 0000000..9b19214
--- /dev/null
+++ b/syn/examples/heapsize/Cargo.toml
@@ -0,0 +1,2 @@
+[workspace]
+members = ["example", "heapsize", "heapsize_derive"]
diff --git a/syn/examples/heapsize/README.md b/syn/examples/heapsize/README.md
new file mode 100644
index 0000000..e789559
--- /dev/null
+++ b/syn/examples/heapsize/README.md
@@ -0,0 +1,72 @@
+A derive macro that generates trait impls.
+
+- [`heapsize/src/lib.rs`](heapsize/src/lib.rs)
+- [`heapsize_derive/src/lib.rs`](heapsize_derive/src/lib.rs)
+- [`example/src/main.rs`](example/src/main.rs)
+
+We are deriving the `HeapSize` trait which computes an estimate of the amount of
+heap memory owned by a value.
+
+```rust
+pub trait HeapSize {
+ /// Total number of bytes of heap memory owned by `self`.
+ fn heap_size_of_children(&self) -> usize;
+}
+```
+
+The derive macro allows users to write `#[derive(HeapSize)]` on data structures
+in their program.
+
+```rust
+#[derive(HeapSize)]
+struct Demo<'a, T: ?Sized> {
+ a: Box<T>,
+ b: u8,
+ c: &'a str,
+ d: String,
+}
+```
+
+The trait impl generated by the derive macro here would look like:
+
+```rust
+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 &mdash; everything is as shown in this directory &mdash;
+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`
+```
diff --git a/syn/examples/heapsize/example/Cargo.toml b/syn/examples/heapsize/example/Cargo.toml
new file mode 100644
index 0000000..85c7699
--- /dev/null
+++ b/syn/examples/heapsize/example/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "heapsize_example"
+version = "0.0.0"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+heapsize = { path = "../heapsize" }
diff --git a/syn/examples/heapsize/example/src/main.rs b/syn/examples/heapsize/example/src/main.rs
new file mode 100644
index 0000000..9332b11
--- /dev/null
+++ b/syn/examples/heapsize/example/src/main.rs
@@ -0,0 +1,28 @@
+use heapsize::HeapSize;
+
+#[derive(HeapSize)]
+struct Demo<'a, T: ?Sized> {
+ a: Box<T>,
+ b: u8,
+ c: &'a str,
+ d: String,
+}
+
+fn main() {
+ let demo = Demo {
+ a: b"bytestring".to_vec().into_boxed_slice(),
+ b: 255,
+ c: "&'static str",
+ d: "String".to_owned(),
+ };
+
+ // 10 + 0 + 0 + 6 = 16
+ println!(
+ "heap size = {} + {} + {} + {} = {}",
+ demo.a.heap_size_of_children(),
+ demo.b.heap_size_of_children(),
+ demo.c.heap_size_of_children(),
+ demo.d.heap_size_of_children(),
+ demo.heap_size_of_children()
+ );
+}
diff --git a/syn/examples/heapsize/heapsize/Cargo.toml b/syn/examples/heapsize/heapsize/Cargo.toml
new file mode 100644
index 0000000..27bb954
--- /dev/null
+++ b/syn/examples/heapsize/heapsize/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "heapsize"
+version = "0.0.0"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+heapsize_derive = { path = "../heapsize_derive" }
diff --git a/syn/examples/heapsize/heapsize/src/lib.rs b/syn/examples/heapsize/heapsize/src/lib.rs
new file mode 100644
index 0000000..30bb6d6
--- /dev/null
+++ b/syn/examples/heapsize/heapsize/src/lib.rs
@@ -0,0 +1,64 @@
+use std::mem;
+
+pub use heapsize_derive::*;
+
+pub trait HeapSize {
+ /// Total number of bytes of heap memory owned by `self`.
+ ///
+ /// Does not include the size of `self` itself, which may or may not be on
+ /// the heap. Includes only children of `self`, meaning things pointed to by
+ /// `self`.
+ fn heap_size_of_children(&self) -> usize;
+}
+
+//
+// In a real version of this library there would be lots more impls here, but
+// here are some interesting ones.
+//
+
+impl HeapSize for u8 {
+ /// A `u8` does not own any heap memory.
+ fn heap_size_of_children(&self) -> usize {
+ 0
+ }
+}
+
+impl HeapSize for String {
+ /// A `String` owns enough heap memory to hold its reserved capacity.
+ fn heap_size_of_children(&self) -> usize {
+ self.capacity()
+ }
+}
+
+impl<T> HeapSize for Box<T>
+where
+ T: ?Sized + HeapSize,
+{
+ /// A `Box` owns however much heap memory was allocated to hold the value of
+ /// type `T` that we placed on the heap, plus transitively however much `T`
+ /// itself owns.
+ fn heap_size_of_children(&self) -> usize {
+ mem::size_of_val(&**self) + (**self).heap_size_of_children()
+ }
+}
+
+impl<T> HeapSize for [T]
+where
+ T: HeapSize,
+{
+ /// Sum of heap memory owned by each element of a dynamically sized slice of
+ /// `T`.
+ fn heap_size_of_children(&self) -> usize {
+ self.iter().map(HeapSize::heap_size_of_children).sum()
+ }
+}
+
+impl<'a, T> HeapSize for &'a T
+where
+ T: ?Sized,
+{
+ /// A shared reference does not own heap memory.
+ fn heap_size_of_children(&self) -> usize {
+ 0
+ }
+}
diff --git a/syn/examples/heapsize/heapsize_derive/Cargo.toml b/syn/examples/heapsize/heapsize_derive/Cargo.toml
new file mode 100644
index 0000000..f4357b9
--- /dev/null
+++ b/syn/examples/heapsize/heapsize_derive/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "heapsize_derive"
+version = "0.0.0"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+publish = false
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { path = "../../.." }
diff --git a/syn/examples/heapsize/heapsize_derive/src/lib.rs b/syn/examples/heapsize/heapsize_derive/src/lib.rs
new file mode 100644
index 0000000..9176b29
--- /dev/null
+++ b/syn/examples/heapsize/heapsize_derive/src/lib.rs
@@ -0,0 +1,96 @@
+extern crate proc_macro;
+
+use proc_macro2::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::spanned::Spanned;
+use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, Index};
+
+#[proc_macro_derive(HeapSize)]
+pub fn derive_heap_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ // Parse the input tokens into a syntax tree.
+ let input = parse_macro_input!(input as DeriveInput);
+
+ // Used in the quasi-quotation below as `#name`.
+ let name = input.ident;
+
+ // Add a bound `T: HeapSize` to every type parameter T.
+ let generics = add_trait_bounds(input.generics);
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ // Generate an expression to sum up the heap size of each field.
+ let sum = heap_size_sum(&input.data);
+
+ let expanded = quote! {
+ // The generated impl.
+ impl #impl_generics heapsize::HeapSize for #name #ty_generics #where_clause {
+ fn heap_size_of_children(&self) -> usize {
+ #sum
+ }
+ }
+ };
+
+ // Hand the output tokens back to the compiler.
+ proc_macro::TokenStream::from(expanded)
+}
+
+// Add a bound `T: HeapSize` to every type parameter T.
+fn add_trait_bounds(mut generics: Generics) -> Generics {
+ for param in &mut generics.params {
+ if let GenericParam::Type(ref mut type_param) = *param {
+ type_param.bounds.push(parse_quote!(heapsize::HeapSize));
+ }
+ }
+ generics
+}
+
+// Generate an expression to sum up the heap size of each field.
+fn heap_size_sum(data: &Data) -> TokenStream {
+ match *data {
+ Data::Struct(ref data) => {
+ match data.fields {
+ Fields::Named(ref fields) => {
+ // Expands to an expression like
+ //
+ // 0 + self.x.heap_size() + self.y.heap_size() + self.z.heap_size()
+ //
+ // but using fully qualified function call syntax.
+ //
+ // We take some care to use the span of each `syn::Field` as
+ // the span of the corresponding `heap_size_of_children`
+ // call. This way if one of the field types does not
+ // implement `HeapSize` then the compiler's error message
+ // underlines which field it is. An example is shown in the
+ // readme of the parent directory.
+ let recurse = fields.named.iter().map(|f| {
+ let name = &f.ident;
+ quote_spanned! {f.span()=>
+ heapsize::HeapSize::heap_size_of_children(&self.#name)
+ }
+ });
+ quote! {
+ 0 #(+ #recurse)*
+ }
+ }
+ Fields::Unnamed(ref fields) => {
+ // Expands to an expression like
+ //
+ // 0 + self.0.heap_size() + self.1.heap_size() + self.2.heap_size()
+ let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
+ let index = Index::from(i);
+ quote_spanned! {f.span()=>
+ heapsize::HeapSize::heap_size_of_children(&self.#index)
+ }
+ });
+ quote! {
+ 0 #(+ #recurse)*
+ }
+ }
+ Fields::Unit => {
+ // Unit structs cannot own more than 0 bytes of heap memory.
+ quote!(0)
+ }
+ }
+ }
+ Data::Enum(_) | Data::Union(_) => unimplemented!(),
+ }
+}