I'd like to write:
#![feature(specialization)]
use std::ops::Range;
trait Trait<T: ?Sized> {}
impl<T> Trait<T> for Range<T> {}
impl<'a, T: ?Sized, C> Trait<T> for C where C: Trait<&'a T> {} // <-- broken
So that, e.g., Trait<str>
is implemented on Range<&str>
. Unfortunately, rust seems to be eagerly evaluating the marked trait implementation. Is there a fundamental reason this can't work (i.e., could rust support this)?
The following works without specialization:
use std::ops::Range;
trait Trait<T: ?Sized> {}
impl<T> Trait<T> for Range<T> {}
impl<'a, T: ?Sized> Trait<T> for Range<&'a T> {}
I'm not quite following what you're trying to do in your original code.
I was trying to work around some specialization issues so I wanted my blanket impl to be strictly more general. After trying to explain it, I've realized it wouldn't work anyways so I'm back where I started...
Basically, I have:
trait OrderedRange<T: ?Sized> where T: Ord {
fn range_cmp(&self, other: &T) -> Ordering;
}
I want the following impls:
// Covers OrderedRange<T> for Range<T>, OrderedRange<Box<T>> for T, etc...
impl<A: ?Sized, B> OrderedRange<A> for Range<B>
where A: Ord + Borrow<B>, B: Ord
{
fn range_cmp(&self, other: &A) -> Ordering {
unimplemented!()
}
}
// Covers OrderedRange<String> for Range<&str>, etc...
// i.e., the unsized borrows.
impl<'a, A: ?Sized, B: ?Sized> OrderedRange<A> for Range<&'a B>
where A: Ord + Borrow<B>, B: Ord
{
fn range_cmp(&self, other: &A) -> Ordering {
unimplemented!()
}
}
However, I can't do this with specialization because neither impl is more specific. So I thought I could replace the second impl with:
/// Means that OrderedRange<&A> implies OrderedRange<A>
impl<'a, T: 'a, A: ?Sized> OrderedRange<A> for T
where T: OrderedRange<&'a A>, A: Ord
{
default fn range_cmp(&self, other: &A) -> Ordering {
(&**self).range_cmp(&other)
}
}
But that (if it worked) would only fix the OrderedRange<T> for Range<&T>
case.
So, I found a solution that works for all the range types (but it's exactly a nice solution)...
#![feature(optin_builtin_traits)]
use std::cmp::Ordering;
use std::ops::{Range, RangeFull};
use std::borrow::Borrow;
use std::marker::PhantomData;
trait OrderedRange<T: ?Sized> where T: Ord {
fn range_cmp(&self, other: &T) -> Ordering;
}
impl<'a, T: ?Sized, R: ?Sized> OrderedRange<T> for &'a R
where R: OrderedRange<T>,
T: Ord,
{
fn range_cmp(&self, other: &T) -> Ordering {
(&**self).range_cmp(other)
}
}
impl<'a, T: ?Sized + 'a> OrderedRange<T> for Range<&'a T>
where T: Ord,
{
fn range_cmp(&self, other: &T) -> Ordering {
if other <= self.start {
Ordering::Greater
} else if other > self.end {
Ordering::Less
} else {
Ordering::Equal
}
}
}
// Helps infer the correct type...
trait NotRef {}
impl NotRef for .. {}
impl<'a, T: ?Sized> !NotRef for &'a T {}
impl<T> OrderedRange<T> for Range<T>
where T: Ord + NotRef,
{
fn range_cmp(&self, other: &T) -> Ordering {
if *other <= self.start {
Ordering::Greater
} else if *other > self.end {
Ordering::Less
} else {
Ordering::Equal
}
}
}
impl<T> OrderedRange<T> for RangeFull where T: Ord {
fn range_cmp(&self, _: &T) -> Ordering {
Ordering::Equal
}
}
struct Set<T>(PhantomData<T>);
impl<T> Set<T> {
fn new() -> Self {
Set(PhantomData)
}
fn query<K: ?Sized, Q>(&self, _query: Q)
where K: Ord,
T: Borrow<K>,
Q: OrderedRange<K>
{}
}
fn main() {
let string_set: Set<String> = Set::new();
let int_set: Set<u32> = Set::new();
string_set.query("a".."b");
string_set.query(String::from("a")..String::from("b"));
string_set.query(&(String::from("a")..String::from("b")));
int_set.query(0..100);
int_set.query(..);
int_set.query(&(..));
}