I want to bound a type based on a function it can reach via deref coercions. Problem is bounding on Deref only works on one level of coercion.
struct A {}
impl A {
fn foo(&self) {
println!("foo");
}
}
struct B {}
impl Deref for B {
type Target = A;
fn deref(&self) -> &Self::Target {
&A{}
}
}
struct C {}
impl Deref for C {
type Target = B;
fn deref(&self) -> &Self::Target {
&B{}
}
}
fn call_with_ref(obj: &A) {
print!("call_with_ref: ");
obj.foo();
}
fn checked_call_with_ref(obj: &A) {
let ptr: *const A = obj;
if !ptr.is_null() {
print!("checked_call_with_ref: ");
obj.foo();
} else {
println!("checked_call_with_ref: null")
}
}
fn call_with_pointer<T: Deref<Target = A>>(ptr: *const T) {
if !ptr.is_null() {
print!("call_with_pointer: ");
unsafe { (*ptr).foo() };
}
}
fn main() {
let c = C{};
let ref_to_c = &c;
let mut ptr_to_c: *const C = ref_to_c;
ref_to_c.foo(); // works
call_with_ref(ref_to_c); // works
// call_with_pointer(ptr_to_C); // does not work bec. it derefs to B
unsafe { checked_call_with_ref(&*ptr_to_c) }; // works on non-nulls
ptr_to_c = std::ptr::null();
unsafe { checked_call_with_ref(&*ptr_to_c) }; // undefined behavior on null pointers
// have to put null check on each call site
if let Some(ref_to_obj) = unsafe { ptr_to_c.as_ref() } {
call_with_ref(ref_to_obj);
}
}
The foo function here is only implemented by A
. Refs to B
can call it with a direct coerce while refs to C
can still call it via deref-ing to B. Deref<Target = A>
is not the proper bound as C
implements Deref<Target = B>
. Does Rust have some sort of Coercible
trait?