Skip to content

Commit d47b1bc

Browse files
committed
Display args values via Debug if no ToString
This abuses inherent impls having priority over trait impls when the compiler chooses which `to_string` method to invoke.
1 parent 39e9c83 commit d47b1bc

File tree

5 files changed

+67
-33
lines changed

5 files changed

+67
-33
lines changed

CHANGELOG.md

+23-9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ Versioning](http://semver.org/spec/v2.0.0.html).
1010

1111
### Added
1212

13+
- Display [`args`] option values with [`Debug`] instead if [`ToString`] is not
14+
implemented.
15+
16+
This makes it simple to use enums with derived [`Debug`]:
17+
18+
```rs
19+
#[derive(Debug)]
20+
enum Arg { A, B }
21+
22+
#[divan::bench(args = [Arg::A, Arg::B])]
23+
fn bench_args(arg: &Arg) {
24+
...
25+
}
26+
```
27+
1328
- Documentation of when to use [`black_box`] in benchmarks.
1429

1530
## [0.1.11] - 2024-01-20
@@ -50,15 +65,6 @@ Versioning](http://semver.org/spec/v2.0.0.html).
5065
- It does not increase compile times, unlike [`consts`] which needs to
5166
generate new code for each constant used.
5267

53-
[`args`]: https://docs.rs/divan/latest/divan/attr.bench.html#args
54-
[`consts`]: https://docs.rs/divan/latest/divan/attr.bench.html#consts
55-
56-
[`Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
57-
[`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
58-
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
59-
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
60-
[`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
61-
6268
## [0.1.8] - 2023-12-19
6369

6470
### Changes
@@ -257,10 +263,18 @@ Initial release. See [blog post](https://nikolaivazquez.com/blog/divan/).
257263
[0.1.1]: https://github.com/nvzqz/divan/compare/v0.1.0...v0.1.1
258264

259265
[`AllocProfiler`]: https://docs.rs/divan/0.1/divan/struct.AllocProfiler.html
266+
[`args`]: https://docs.rs/divan/latest/divan/attr.bench.html#args
260267
[`black_box`]: https://docs.rs/divan/latest/divan/fn.black_box.html
268+
[`consts`]: https://docs.rs/divan/latest/divan/attr.bench.html#consts
261269

270+
[`Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
271+
[`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
272+
[`Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
262273
[`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html
263274
[`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
275+
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
276+
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
277+
[`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html
264278
[available parallelism]: https://doc.rust-lang.org/std/thread/fn.available_parallelism.html
265279
[drop_fn]: https://doc.rust-lang.org/std/mem/fn.drop.html
266280
[`thread_local!`]: https://doc.rust-lang.org/std/macro.thread_local.html

macros/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ pub fn bench(options: TokenStream, item: TokenStream) -> TokenStream {
247247
#bench_entry_runner::Args(|| __DIVAN_ARGS.runner(
248248
|| #arg_return_tokens { #args },
249249

250+
|arg| #private_mod::ToStringHelper(arg).to_string(),
251+
250252
|divan, __divan_arg| divan.bench(|| #fn_expr(
251253
#private_mod::Arg::<#last_arg_type_tokens>::get(__divan_arg)
252254
)),
@@ -258,6 +260,8 @@ pub fn bench(options: TokenStream, item: TokenStream) -> TokenStream {
258260
#bench_entry_runner::Args(|| __DIVAN_ARGS.runner(
259261
|| #arg_return_tokens { #args },
260262

263+
|arg| #private_mod::ToStringHelper(arg).to_string(),
264+
261265
|divan, __divan_arg| #fn_expr(
262266
divan,
263267
#private_mod::Arg::<#last_arg_type_tokens>::get(__divan_arg),

src/bench/args.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ impl BenchArgs {
5858
pub fn runner<I, B>(
5959
&'static self,
6060
make_args: impl FnOnce() -> I,
61+
arg_to_string: impl Fn(&I::Item) -> String,
6162
_bench_impl: B,
6263
) -> BenchArgsRunner
6364
where
6465
I: IntoIterator,
65-
I::Item: Any + ToString + Send + Sync,
66+
I::Item: Any + Send + Sync,
6667
B: FnOnce(Bencher, &I::Item) + Copy,
6768
{
6869
let args = self.args.get_or_init(|| {
@@ -90,7 +91,7 @@ impl BenchArgs {
9091
return arg;
9192
}
9293

93-
Box::leak(arg.to_string().into_boxed_str())
94+
Box::leak(arg_to_string(arg).into_boxed_str())
9495
})
9596
.collect(),
9697
)
@@ -205,7 +206,7 @@ mod tests {
205206
fn str() {
206207
static ARGS: BenchArgs = BenchArgs::new();
207208

208-
let runner = ARGS.runner(|| ["a", "b"], |_, _| {});
209+
let runner = ARGS.runner(|| ["a", "b"], ToString::to_string, |_, _| {});
209210

210211
let typed_args = runner.args.typed_args::<&str>().unwrap();
211212
let names = runner.arg_names();
@@ -219,7 +220,8 @@ mod tests {
219220
fn string() {
220221
static ARGS: BenchArgs = BenchArgs::new();
221222

222-
let runner = ARGS.runner(|| ["a".to_owned(), "b".to_owned()], |_, _| {});
223+
let runner =
224+
ARGS.runner(|| ["a".to_owned(), "b".to_owned()], ToString::to_string, |_, _| {});
223225

224226
let typed_args = runner.args.typed_args::<String>().unwrap();
225227
let names = runner.arg_names();
@@ -234,6 +236,7 @@ mod tests {
234236

235237
let runner = ARGS.runner(
236238
|| ["a".to_owned().into_boxed_str(), "b".to_owned().into_boxed_str()],
239+
ToString::to_string,
237240
|_, _| {},
238241
);
239242

@@ -248,8 +251,11 @@ mod tests {
248251
fn cow_str() {
249252
static ARGS: BenchArgs = BenchArgs::new();
250253

251-
let runner =
252-
ARGS.runner(|| [Cow::Owned("a".to_owned()), Cow::Borrowed("b")], |_, _| {});
254+
let runner = ARGS.runner(
255+
|| [Cow::Owned("a".to_owned()), Cow::Borrowed("b")],
256+
ToString::to_string,
257+
|_, _| {},
258+
);
253259

254260
let typed_args = runner.args.typed_args::<Cow<str>>().unwrap();
255261
let names = runner.arg_names();

src/lib.rs

+4-17
Original file line numberDiff line numberDiff line change
@@ -358,22 +358,15 @@ pub fn black_box_drop<T>(dummy: T) {
358358
/// ```
359359
///
360360
/// Unlike the [`consts`] option, any argument type is supported if it
361-
/// implements [`Any`], [`Copy`], [`Send`], [`Sync`], and [`ToString`]:
361+
/// implements [`Any`], [`Copy`], [`Send`], [`Sync`], and [`ToString`] (or
362+
/// [`Debug`](std::fmt::Debug)):
362363
///
363364
/// ```
364-
/// #[derive(Clone, Copy)]
365+
/// #[derive(Clone, Copy, Debug)]
365366
/// enum Arg {
366367
/// A, B
367368
/// }
368369
///
369-
/// // `Display` implies `ToString`.
370-
/// impl std::fmt::Display for Arg {
371-
/// // ...
372-
/// # fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
373-
/// # Ok(())
374-
/// # }
375-
/// }
376-
///
377370
/// #[divan::bench(args = [Arg::A, Arg::B])]
378371
/// fn bench_args(arg: Arg) {
379372
/// // ...
@@ -384,17 +377,11 @@ pub fn black_box_drop<T>(dummy: T) {
384377
/// a reference:
385378
///
386379
/// ```
380+
/// #[derive(Debug)]
387381
/// enum Arg {
388382
/// A, B
389383
/// }
390384
///
391-
/// impl std::fmt::Display for Arg {
392-
/// // ...
393-
/// # fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
394-
/// # Ok(())
395-
/// # }
396-
/// }
397-
///
398385
/// #[divan::bench(args = [Arg::A, Arg::B])]
399386
/// fn bench_args(arg: &Arg) {
400387
/// // ...

src/private.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::borrow::Borrow;
21
pub use std::{
32
self, any, borrow::Cow, default::Default, iter::FromIterator, option::Option::*, sync::OnceLock,
43
};
4+
use std::{borrow::Borrow, fmt::Debug};
55

66
pub use crate::{
77
bench::{BenchArgs, BenchOptions},
@@ -12,6 +12,29 @@ pub use crate::{
1212
time::IntoDuration,
1313
};
1414

15+
/// Helper to convert values to strings via `ToString` or fallback to `Debug`.
16+
///
17+
/// This works by having a `Debug`-based `ToString::to_string` method that will
18+
/// be chosen if the wrapped type implements `Debug` *but not* `ToString`. If
19+
/// the wrapped type implements `ToString`, then the inherent
20+
/// `ToStringHelper::to_string` method will be chosen instead.
21+
pub struct ToStringHelper<'a, T: 'static>(pub &'a T);
22+
23+
impl<T: Debug> ToString for ToStringHelper<'_, T> {
24+
#[inline]
25+
fn to_string(&self) -> String {
26+
format!("{:?}", self.0)
27+
}
28+
}
29+
30+
impl<T: ToString> ToStringHelper<'_, T> {
31+
#[allow(clippy::inherent_to_string)]
32+
#[inline]
33+
pub fn to_string(&self) -> String {
34+
self.0.to_string()
35+
}
36+
}
37+
1538
/// Used by `#[divan::bench(args = ...)]` to enable polymorphism.
1639
pub trait Arg<T> {
1740
fn get(self) -> T;

0 commit comments

Comments
 (0)