Hi, I'm trying to implement a function takes something that can deref to an i32, but I don't know how to get it to work. Here is a minimal example:
fn main() {
let nested_pointer = Arc::new(Arc::new(2i32));
let sucess: &i32 = &nested_pointer;
let fail: &i32 = foo(&nested_pointer);
}
fn foo<T: Deref<Target=i32>>(t: &T) -> &i32 {
t
}
This does not compile and gives error:
error[E0271]: type mismatch resolving `<Arc<Arc<i32>> as Deref>::Target == i32`
--> src/main.rs:7:22
|
7 | let fail: &i32 = foo(&nested_pointer);
| ^^^ expected `i32`, found struct `Arc`
|
= note: expected type `i32`
found struct `Arc<i32>`
I think I know why it doesn't work. But I don't know how to make it work.
Here is my use case:
There a type T,
Some part of the program wraps T with some extra data.
I want to have a collection that stores a bunch of wrapper of T
The collection cares what's in T
The collection does not care what the wrapper is.
All elements in the collection will have the same wrapper.
This fails because the type of nested_pointer is Arc<Arc<i32>>, and the bound T: Deref<Target=i32> is not met. So solutions are simple:
// explicit dereferencing: the type of `&*nested_pointer` is `&Arc<i32>`, thus the trait bound is met
let tmp1: &i32 = foo(&*nested_pointer);
// or specify the outer wrapper type
let tmp2: &i32 = foo2(&nested_pointer);
fn foo2<T: Deref<Target = i32>>(t: &Arc<T>) -> &i32 {
t
}
// or abstract the outer & inner wrapper type
let tmp3: &i32 = foo3(&nested_pointer);
fn foo3<'a, T: Deref<Target = i32> + 'a, U: Deref<Target = T>>(t: &'a U) -> &'a i32 {
t
}
Thank you for answering. I know I explicitly do that. My question is, is there a way to implement foo such that foo<&32>, foo<&Arc<i32>> and foo<&Arc<Arc<i32>>> all work? The type coercion system is able to handle it, so I'm guessing type bound can also handle it.
I want EvenOdd to be able to store any type that derefs to Foo. At least should make EvenOdd<Arc<Foo>>, EvenOdd<Arc<Arc<Foo>>> to all work. Having EvenOdd implement both single deref and double deref should work, but it feels clunky.
After some experimentation, I found something that sort of works. But without negative type bound, it requires the inner type to manually implement a trait.
use std::sync::Arc;
use core::ops::Deref;
trait RecDeref<'a, T> {
fn rec_deref(&'a self) -> &'a T;
}
struct Wrapper {
i: i32,
}
/** Can be replaced by a generic impl block over Wrapper if negative type bound is supported */
impl<'a> RecDeref<'a, Wrapper> for Wrapper {
fn rec_deref(&'a self) -> &'a Self {
self
}
}
impl<'a, A,B,C> RecDeref<'a, A> for C
where C: Deref<Target = B>, B: RecDeref<'a, A> + 'a
{
fn rec_deref(&'a self) -> &'a A {
RecDeref::<A>::rec_deref(Deref::deref(self))
}
}
fn bar<'a, B: RecDeref<'a, Wrapper>>(b: &'a B) -> &'a Wrapper {
b.rec_deref()
}
fn main() {
let foo = Arc::new(Arc::new(Arc::new(Wrapper { i: 2i32 })));
let result1 = bar(&foo);
println!("{}", result1.i);
}
error[E0277]: the trait bound `Arc<Arc<{integer}>>: AsRef<i32>` is not satisfied
--> src/main.rs:14:9
|
14 | foo(Arc::new(Arc::new(14)));
| --- ^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<i32>` is not implemented for `Arc<Arc<{integer}>>`
| |
| required by a bound introduced by this call
|
= help: the trait `AsRef<T>` is implemented for `Arc<T>`
note: required by a bound in `foo`
--> src/main.rs:7:10
|
5 | fn foo<Ptr>(arg: Ptr)
| --- required by a bound in this
6 | where
7 | Ptr: AsRef<i32>,
| ^^^^^^^^^^ required by this bound in `foo`
I think I self answered the question with RecDeref. It's probably the best solution for current rust.
The main issue with your solution is that you have to explicitly say two layers of deref/borrow has to be done, which I want to avoid. I want it to support arbitrary number of layers of deref, which is also what type coercion does.
If I were trying to do something like this I think I'd just use an enum, as there's a bit of readability vomit that I get when going through all the &** derefing.. When I see it in the std-library layers I just assume there's no other performant way, but at this layer, I don't think an enum or dyn indirection is going to be as killer. Readability is likely more important. Sounds like you might have even needed a Cow<'a, T> as one of the varients. Some ugliness (but explicitness) may prevent some literal head-aches down the road.