Skip to content

Instantly share code, notes, and snippets.

@tesaguri
Created October 10, 2021 12:24
Show Gist options
  • Save tesaguri/e14a9733a482869bb5665675838ce3c9 to your computer and use it in GitHub Desktop.
Save tesaguri/e14a9733a482869bb5665675838ce3c9 to your computer and use it in GitHub Desktop.
`x.to_string().cmp(&y.to_string())` without allocating the string (not throughly tested yet)
use std::cmp::Ordering;
use std::fmt::{self, Display, Write};
struct FmtCmp<T>(T);
impl<T: Display> PartialEq for FmtCmp<T> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl<T: Display> Eq for FmtCmp<T> {}
impl<T: Display> PartialOrd for FmtCmp<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: Display> Ord for FmtCmp<T> {
fn cmp(&self, other: &Self) -> Ordering {
struct Lhs<T> {
this: T,
/// Byte position in `this.to_string()` that we are reading.
pos: usize,
other_len: usize,
ret: Ordering,
}
let mut adapter = Lhs {
this: &self.0,
pos: 0,
other_len: 0,
ret: Ordering::Equal,
};
let _ = write!(&mut adapter, "{}", &other.0);
return adapter.ret.then(adapter.pos.cmp(&adapter.other_len));
impl<T: Display> Write for Lhs<T> {
fn write_str(&mut self, other: &str) -> fmt::Result {
struct Rhs<'a> {
other: &'a [u8],
/// Number of bytes to skip until we get to `this.to_string()[pos]`.
skip: usize,
ret: &'a mut Ordering,
}
let mut adapter = Rhs {
other: other.as_bytes(),
skip: self.pos,
ret: &mut self.ret,
};
write!(&mut adapter, "{}", self.this)?;
if !adapter.other.is_empty() {
// `other` is longer than `this`.
self.ret = self.ret.then(Ordering::Less);
}
if self.ret != Ordering::Equal {
// Short-circuit by returning an error.
return Err(fmt::Error);
}
self.pos += other.len();
self.other_len += other.len();
return Ok(());
impl<'a> Write for Rhs<'a> {
fn write_str(&mut self, this: &str) -> fmt::Result {
let skip = self.skip.min(this.len());
self.skip -= skip;
let this = &this.as_bytes()[skip..];
let read = this.len().min(self.other.len());
*self.ret = this.cmp(&self.other[0..read]);
if *self.ret != Ordering::Equal {
return Err(fmt::Error);
}
self.other = &self.other[read..];
Ok(())
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt_cmp() {
#[track_caller]
fn check<T: Display>(a: T, b: T) {
assert_eq!(
FmtCmp(&a).cmp(&FmtCmp(&b)),
a.to_string().cmp(&b.to_string()),
);
}
check("", "");
check(42, 42);
check(42, 240);
check(240, 42);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment