Is there a way to write the below where f is a generic that can return either a Box or Arc?
use std::sync::Arc;
trait A {}
struct A1 {}
impl A for A1 {}
fn f(i: &dyn A) -> Box<dyn A> {
return Box::new(A1 {})
}
fn main() {
let a = A1 {};
let a = &a as &dyn A;
let boxed: Box<dyn A> = f(a);
let arced: Arc<dyn A> = f(a);
}
I don't think there's currently a direct way, especially since it involves coercing to the unsized type. You can use Into to convert the Box to Arc, f(a).into(), but that does internally move to a new heap allocation so it can store the counters with the value.
Got it. that is what I am doing atm: offer the most generic version (Box), and ask users to write .into() when they want Arc, but like you wrote, that does introduce a level of indirection.
@H2CO3 yes, you can do that, but it still has to allocate twice to arrive at Arc. Also, the ergonomic tradeoff is not an obvious win -- you avoid into(), but you introduce ambiguity to type inference.
// these will get `T` from the explicit type annotation
let boxed: Box<dyn A> = f(a);
let arced: Arc<dyn A> = f(a);
// this will be ambiguous
let dunno = f(a);
I don't think it's possible to cast them cheaply, because memory layouts of Box and Arc are different, so it has to have two versions of the code that build two different types in their own ways.
let _: Box<A1> = new_a1();
let _: Arc<A1> = new_a1();
Just Works™.
Although at the cost of always requiring an explicit type annotation to tell Rust which pointer to pick (there is no "type fallback" mechanism on functions yet).
The difficult part is actually the A1 -> dyn A part behind a pointer.
Either the trait above is rewritten with an explicit A1 and dyn A parameters,
and then the f() function would have a P : HeapAllocatedPtr<A1, dyn A> kind of bound (which thus breaks abstraction and is not a good solution),
Or we commit to nightly to have a way to be generic over types coercible to a generic trait object. The nightly requirement is not great either.
With the latter approach, the following works on nightly Rust:
#![feature(unsize)]
use ::std::marker::Unsize;
trait HeapAllocatedPointer<DynTrait : ?Sized> {
fn new<T : Unsize<DynTrait>> (value: T)
-> Self
;
}
/* impls … */
trait A {}
fn f<Ptr> ()
-> Ptr
where
Ptr : HeapAllocatedPointer<dyn A>,
{
Ptr::new({
struct A1;
impl A for A1 {}
A1
})
}
fn main ()
{
let _: Box<dyn A> = f();
let _: Arc<dyn A> = f();
}