Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This changes the `valuable` integration so that `record_value` takes instances of `valuable::Value<'_>`, rather than `&dyn valuable::Valuable` trait objects. The primary advantage of this is that a `Value` can be produced by calling `Valuable::as_value`, so it allows users to write code like this: ```rust #[derive(Valuable, Debug)] struct Foo { // ... } tracing::trace!(foo = foo.as_value()); ``` rather than this: ```rust #[derive(Valuable, Debug)] struct Foo { // ... } tracing::trace!(foo = tracing::field::valuable(&foo)); ``` which feels a bit more ergonomic. It also simplifies the code in `tracing-core`, since we no longer need our own `ValuableValue` wrapper type to turn things into trait objects. It might also reduce boilerplate a bit on the visitor side, as `as_value()` doesn't have to be called on the trait object, although that's probably not as big a deal. I didn't remove the `field::valuable` function, as I thought it was nice to have for consistency with the existing `field::debug` and `field::display` functions. ## Performance Considerations @carllerche pointed out that a `Value<'_>` might be slightly more bytes to pass on the stack than a trait object (always two words). I believe this is only the case when the `Value` is a `Listable`, `Enumerable`, `Structable`, `Mappable`, or `Tupleable`, where the `Value` would be an enum descriminant _and_ a wide pointer to a trait object. However, in the cases where the value is a primitive, `Value` will be two words if the primitive is word-sized (e.g. `u64` on 64-bit platforms), for the enum descriminant + the value, or one word if the primitive is smaller than word size (`bool`, `char`, etc). Also, for primitive `Value`s, there's no pointer dereference, which the trait object always requires. I'm not sure how the enum dispatch compares to vtable dispatch when calling `visit` on the value. However, if the `tracing` visitor is going to call `as_value()` on the recorded value, this approach is better, because calling `as_value()` in the macro _prior_ to recording the span/event will use the statically dispatched `as_value()` impl on a known type, rather than the the dynamically dispatched `as_value()` impl on the trait object. Since `as_value` impls are generally quite trivial, I'd guess they usually (always?) will get inlined, which is never possible with the dynamically dispatched call after passing a trait object into `tracing`. In practice I'm not sure if there's a huge perf diff either way, but it was interesting to think through the implications.
- Loading branch information