Skip to content

Commit fa4fece

Browse files
authored
subscriber: make PartialOrd & Ord impls more correct (tokio-rs#995)
## Motivation Currently, there are some minor issues with `Ord` and `PartialOrd` impls in `tracing_subscriber::filter::env`: - The `Directive` and `StaticDirective` types implement `PartialOrd` with an implementation that never returns `None`, and then have `Ord` implementations that call `partial_cmp` and `expect` that the returned value is `Some`. This isn't necessary. - `field::Match` implements `PartialOrd` manually but derives `Ord`. Since these traits must agree, using the derived implementation for one but manually implementing the other is potentially incorrect (see tokio-rs#991). ## Solution This branch fixes these issues. I've moved actual comparison code from `PartialOrd` impls for `Directive` and `StaticDirective` to their `Ord` impls, and changed `PartialOrd::partial_cmp` for those types to call `Ord::cmp` and wrap it in a `Some`, rather than having `Ord::cmp` call `PartialOrd::partial_cmp` and unwrap it. This way, the fact that these comparison impls can never return `None` is encoded at the type level, rather than having an `expect` that will always succeed. Additionally, I've added a manual impl of `Ord` for `field::Match` and removed the derived impl. This should make Clippy happier. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
1 parent 4bbdc7c commit fa4fece

File tree

2 files changed

+25
-23
lines changed

2 files changed

+25
-23
lines changed

tracing-subscriber/src/filter/env/directive.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ impl Default for Directive {
275275

276276
impl PartialOrd for Directive {
277277
fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
278+
Some(self.cmp(other))
279+
}
280+
}
281+
282+
impl Ord for Directive {
283+
fn cmp(&self, other: &Directive) -> Ordering {
278284
// We attempt to order directives by how "specific" they are. This
279285
// ensures that we try the most specific directives first when
280286
// attempting to match a piece of metadata.
@@ -321,14 +327,7 @@ impl PartialOrd for Directive {
321327
}
322328
}
323329

324-
Some(ordering)
325-
}
326-
}
327-
328-
impl Ord for Directive {
329-
fn cmp(&self, other: &Self) -> Ordering {
330-
self.partial_cmp(other)
331-
.expect("Directive::partial_cmp should define a total order")
330+
ordering
332331
}
333332
}
334333

@@ -502,8 +501,8 @@ impl Statics {
502501
}
503502
}
504503

505-
impl PartialOrd for StaticDirective {
506-
fn partial_cmp(&self, other: &StaticDirective) -> Option<Ordering> {
504+
impl Ord for StaticDirective {
505+
fn cmp(&self, other: &StaticDirective) -> Ordering {
507506
// We attempt to order directives by how "specific" they are. This
508507
// ensures that we try the most specific directives first when
509508
// attempting to match a piece of metadata.
@@ -542,14 +541,13 @@ impl PartialOrd for StaticDirective {
542541
}
543542
}
544543

545-
Some(ordering)
544+
ordering
546545
}
547546
}
548547

549-
impl Ord for StaticDirective {
550-
fn cmp(&self, other: &Self) -> Ordering {
551-
self.partial_cmp(other)
552-
.expect("StaticDirective::partial_cmp should define a total order")
548+
impl PartialOrd for StaticDirective {
549+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
550+
Some(self.cmp(other))
553551
}
554552
}
555553

tracing-subscriber/src/filter/env/field.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::{
1313
use super::{FieldMap, LevelFilter};
1414
use tracing_core::field::{Field, Visit};
1515

16-
#[derive(Debug, Eq, PartialEq, Ord)]
16+
#[derive(Debug, Eq, PartialEq)]
1717
pub(crate) struct Match {
1818
pub(crate) name: String, // TODO: allow match patterns for names?
1919
pub(crate) value: Option<ValueMatch>,
@@ -95,8 +95,8 @@ impl fmt::Display for Match {
9595
}
9696
}
9797

98-
impl PartialOrd for Match {
99-
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
98+
impl Ord for Match {
99+
fn cmp(&self, other: &Self) -> Ordering {
100100
// Ordering for `Match` directives is based first on _whether_ a value
101101
// is matched or not. This is semantically meaningful --- we would
102102
// prefer to check directives that match values first as they are more
@@ -113,11 +113,15 @@ impl PartialOrd for Match {
113113
// This ordering is no longer semantically meaningful but is necessary
114114
// so that the directives can be stored in the `BTreeMap` in a defined
115115
// order.
116-
Some(
117-
has_value
118-
.then_with(|| self.name.cmp(&other.name))
119-
.then_with(|| self.value.cmp(&other.value)),
120-
)
116+
has_value
117+
.then_with(|| self.name.cmp(&other.name))
118+
.then_with(|| self.value.cmp(&other.value))
119+
}
120+
}
121+
122+
impl PartialOrd for Match {
123+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
124+
Some(self.cmp(other))
121125
}
122126
}
123127

0 commit comments

Comments
 (0)