allocative/
allocative_trait.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::Visitor;

/// This trait allows traversal of object graph.
///
/// # Proc macro
///
/// Typically implemented with proc macro. Like this:
///
/// ```
/// use allocative::Allocative;
///
/// #[derive(Allocative)]
/// struct Foo {
///     x: u32,
///     y: String,
/// }
/// ```
///
/// Proc macro supports two attributes: `#[allocative(skip)]` and
/// `#[allocative(bound = "...")]`.
///
/// ## `#[allocative(skip)]`
///
/// `#[allocative(skip)]` can be used to skip field from traversal (for example,
/// to skip fields which are not `Allocative`, and can be skipped because they
/// are cheap).
///
/// ```
/// use allocative::Allocative;
///
/// /// This does not implement `Allocative`.
/// struct Unsupported;
///
/// #[derive(Allocative)]
/// struct Bar {
///     #[allocative(skip)]
///     unsupported: Unsupported,
/// }
/// ```
///
/// ## `#[allocative(bound = "...")]`
///
/// `#[allocative(bound = "...")]` can be used to overwrite the bounds that are
/// added to the generics of the implementation.
///
/// An empty string (`#[allocative(bound = "")]`) simply erases all bounds. It
/// adds all type variables found in the type to the list of generics but with
/// an empty bound. As an example
///
///
/// ```
/// use std::marker::PhantomData;
///
/// use allocative::Allocative;
///
/// struct Unsupported;
///
/// #[derive(Allocative)]
/// #[allocative(bound = "")]
/// struct Baz<T> {
///     _marker: PhantomData<T>,
/// }
/// ```
///
/// Would generate an instance
///
/// ```ignore
/// impl<T> Allocative for Baz<T> { ... }
/// ```
///
/// Alternatively you can use the string to provide custom bounds. The string in
/// this case is used *verbatim* as the bounds, which affords great flexibility,
/// but also necessitates that all type variables must be mentioned or will be
/// unbound (compile error). As an example we may derive a size of a `HashMap`
/// by ignoring the hasher type.
///
///
/// ```ignore
/// #[allocative(bound = "K: Allocative, V:Allocative, S")]
/// struct HashMap<K, V, S = RandomState> {
///    ...
/// }
/// ```
///
/// Which generates
///
/// ```ignore
/// impl<K: Allocative, V:Allocative, S> Allocative for HashMap<K, V, S> {
///    ...
/// }
/// ```
///
/// ## `#[allocative(visit = ...)]`
///
/// This annotation is used to provide a custom visit method for a given field. This
/// is especially useful if the type of the field does not implement `Allocative`.
///
/// The annotation takes the path to a method with a signature `for<'a, 'b>(&T, &'a
/// mut allocative::Visitor<'b>)` where `T` is the type of the field. The function
/// you provide is basically the same as if you implemented [`Allocative::visit`].
///
/// As an example
///
/// ```
/// use allocative::Allocative;
/// use allocative::Key;
/// use allocative::Visitor;
/// // use third_party_lib::Unsupported;
/// # struct Unsupported<T>(T);
/// # impl<T> Unsupported<T> {
/// #   fn iter_elems(&self) -> &[T] { &[] }
/// # }
///
/// #[derive(Allocative)]
/// struct Bar {
///     #[allocative(visit = visit_unsupported)]
///     unsupported: Unsupported<usize>,
/// }
///
/// fn visit_unsupported<'a, 'b>(u: &Unsupported<usize>, visitor: &'a mut Visitor<'b>) {
///     const ELEM_KEY: Key = Key::new("elements");
///     let mut visitor = visitor.enter_self(u);
///     for element in u.iter_elems() {
///         visitor.visit_field(ELEM_KEY, element);
///     }
///     visitor.exit()
/// }
/// ```
pub trait Allocative {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>);
}