Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to tell the compiler that nobody will implement a trait for a reference to a generic type?

Tags:

rust

Here is toy code that demonstrates the problem:

trait Foo {}

trait Boo<T> {
    fn f() -> T;
}

impl<T> Boo<T> for i32
where
    T: Foo,
{
    fn f() -> T {
        unimplemented!();
    }
}

impl<'a, T> Boo<&'a T> for i32
where
    T: Foo,
{
    fn f() -> T {
        unimplemented!();
    }
}

I want to have two generic implementations of trait Boo, but it doesn't compile:

error[E0119]: conflicting implementations of trait `Boo<&_>` for type `i32`:
  --> src/main.rs:16:1
   |
7  | / impl<T> Boo<T> for i32
8  | | where
9  | |     T: Foo,
10 | | {
...  |
13 | |     }
14 | | }
   | |_- first implementation here
15 | 
16 | / impl<'a, T> Boo<&'a T> for i32
17 | | where
18 | |     T: Foo,
19 | | {
...  |
22 | |     }
23 | | }
   | |_^ conflicting implementation for `i32`
   |
   = note: downstream crates may implement trait `Foo` for type `&_`

I do not plan to make this part of functionality to other crates. I tried:

  • moving this code to binary crate that obviously can not be used from other crates
  • moving this to a private mod
  • marking the trait as pub(crate)

all with no success.

Is there anyway to give the compiler a hint that it should not care that anybody will implement Foo for any reference?

Maybe my toy example is not the best, so here is the real code. It's used for integration with the C part of my program, so it's a little complicated.

impl<T: MyTrait> MyFrom<Option<T>> for *mut c_void {
    fn my_from(x: Option<T>) -> Self {
        match x {
            Some(x) => <T>::alloc_heap_for(x),
            None => ptr::null_mut(),
        }
    }
}

impl<'a, T: MyTrait> MyFrom<Option<&'a T>> for *mut c_void {
    fn my_from(x: Option<&'a T>) -> Self {
        match x {
            Some(x) => x as *const T as *mut c_void,
            None => ptr::null_mut(),
        }
    }
}
like image 390
user1244932 Avatar asked Apr 25 '18 01:04

user1244932


1 Answers

The conflict here doesn't have anything to do with the reference-ness of the latter implementation. The issue is that, in the first implementation, T can be any type, including reference types. Suppose you make the following function call:

let x: i32 = 10;
let result: &u8 = x.f();

At this point, the type resolver needs to figure out what function is being called. It finds a conflicting implementation:

impl Boo<&u8> for i32 via Boo<T>  (T == &u8),
impl Boo<&u8> for i32 via Boo<&T> (T == u8),

You'd have exactly the same issue if you used a concrete type in the latter implementation:

// This will fail to compile
impl<T> Boo<T> for i32 { ... }
impl Boo<String> for i32 { ... }

This conflict means that the compiler can't allow these two implementations to coexist.


The specific thing you're looking to do here is called "specialization"; it refers to a proposal for a set of rules that says that overlapping implementations like this are allowed to exist if one of them is unambiguously more "specific" than the other, in which case the compiler will pick the more specific implementation. This is tracked as RFC #1210.

like image 135
Lucretiel Avatar answered Oct 13 '22 00:10

Lucretiel