I have the following 2 traits: Org, Capability.
#![feature(specialization)]
trait Org {}
struct OrgV1 {}
impl Org for OrgV1 {...}
struct OrgV2 {}
impl Org for OrgV2 {...}
trait Capability {}
struct CapV1 {}
impl Capability for CapV1 {...}
struct CapV2 {}
impl Capability for CapV2 {...}
// Capability has a generic associated .ensure method that is able to handle
// any combination of the concrete types of Org and Capability.
// It is implemented with rust's specialization feature: https://rust-lang.github.io/rfcs/1210-impl-specialization.html
trait CapabilityService<O: Org> where Self: Capability {
fn ensure(self, org_config: Arc<O>) -> Result<bool>
}
impl<O: Org, C: Capability> CapabilityService<O> for C {
default fn ensure(self, org_config: Arc<O>) -> Result<bool> {
... // not so important logic
return Ok(true)
}
}
fn main() {
...
// assume I have the following 2 variables I got in runtime by serde's deserealization:
// org_trait: instance Org trait object
// capability_trait: instance Capability trait object
// Given that there is .ensure that can handle any combination of the concrete repo and org
// is there a way in rust to downcast to a generic concrete type, aka:
let concrete_org = org_trait.as_any().downcast_ref::<T: Org>().unwrap()
let concrete_capability = capability_trait.as_any().downcast_ref::<T: Capability>().unwrap()
// where concrete_org and concrete_capability are some concrete types of Org,
// and Capability respectively.
let result = concrete_capability.ensure(concrete_org)
}
I am aware that I could do something like this:
{
if let Some(org) = org_trait.as_any().downcast_ref::<OrgV1>() {
if Some(cap) = cap_trait.as_any().downcast_ref::<CapV1>() {
cap.ensure(org)
}
...
} else Some(org) = org_trait.as_any().downcast_ref::<OrgV2>() {
...
}
...
}
But it seems like a nightmare to handle once there are more Org/Cap implementations(in my actual case, I have 3 traits that ensure works on, which I didn't include to make the picture a bit clear)
Is it possible to downcast trait to a generic concrete type, given the constraints(since we know that .ensure would be able to be called no matter what the concrete types are)? I would like to avoid writing my own long chain of nested if let Somes if possible
Thanks all. It seems like I have misunderstood how rust's specialization feature works. Specifically, my thought process was opposite of the note mentioned on the RFCs page: This default impl does not mean that Add is implemented for all Clone data, but just that when you do impl Add and Self: Clone, you can leave off add_assign
Related
This question already has answers here:
How to get a reference to a concrete type from a trait object?
(2 answers)
Closed 6 months ago.
I have different structs which I am storing in a HashMap. These structs are sharing similar methods, but have also their own specific methods. I want to store all the different structs in a HashMap, and be able to access both the trait methods and the specific methods belonging to each struct.
As of now, I've found a great way of using the trait methods of structs stored in a HashMap, but my problem is now to access the methods belonging to each specific struct.
This is a very simplified structure of my code:
struct A {value_only_A_has: f64, common_value: u32}
impl A {
fn method_a() {
// Does something very specific and only related to A...
}
}
impl Common for A {
fn method_common(&mut self) -> () {
// Does something which is related to all structs, but properties are only
// related to A (the reason for using traits).
}
}
struct B {value_only_B_has: f64, common_value: u32}
impl B {
fn method_b() {
// Does something very specific and only related to B...
}
}
impl Common for B {
fn method_common(&mut self) -> () {
// Does something which is related to all structs, but properties are only
// related to B (the reason for using traits).
}
}
trait Common {
// Does something which is related to all structs.
fn method_common(&mut self) -> ();
}
This is a very simplified explanation of what I want to do:
// Store all different kind of structures in a HashMap container.
let mut container: HashMap<u32, Box<dyn Common>> = HashMap::new();
let a = A {value_only_A_has: 10.0, common_value: 3};
let b = B {value_only_B_has: 20.0, common_value: 2};
container.insert(0, Box::new(a));
container.insert(1, Box::new(b));
// This trait method is easy to access (this works):
container.get(&1).unwrap().method_common();
// I want to somehow get access to the method
// belonging to each struct (this does not work):
match container.get(&1).unwrap() {
A(v) => v.method_a(),
B(v) => v.method_b(),
}
How can I access the methods specific to each struct by using something ideally similar to this?
(Edit: After playing around with Box, it seems that the only thing stored in container are the trait methods, and not the actual struct with the specific methods. Maybe my approach by storing the structs in container as trait and Box in the way I've done it is wrong?)
A possible approach is to use enum for value types:
use std::collections::HashMap;
enum CommonValue {
A,
B,
}
...
fn main() {
let mut hello: HashMap<u32, CommonValue> = HashMap::new();
...
match hello.get(&1).unwrap() {
CommonValue::A(v) => v.method_a(),
CommonValue::B(v) => v.method_b(),
}
}
Also, if you want to run .method_common on CommonValue, you can implement Common trait for the enum CommonValue:
impl Common for CommonValue {
fn method_common(&mut self) -> () {
// ...
}
}
...
hello.get_mut(&1).unwrap().method_common();
Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).
I want to offer a safe API like below FooManager. It should be able to store arbitrary user-defined values that implement a trait Foo. It should also be able to hand them back later - not as trait object (Box<dyn Foo>) but as the original type (Box<T> where T: Foo). At least conceptually it should be possible to offer this as a safe API, by using generic handles (Handle<T>), see below.
Additional criteria:
The solution should work in stable Rust (internal usage of unsafe blocks is perfectly okay though).
I don't want to modify the trait Foo, as e.g. suggested in How to get a reference to a concrete type from a trait object?. It should work without adding a method as_any(). Reasoning: Foo shouldn't have any knowledge about the fact that it might be stored in containers and be restored to the actual type.
trait Foo {}
struct Handle<T> {
// ...
}
struct FooManager {
// ...
}
impl FooManager {
// A real-world API would complain if the value is already stored.
pub fn keep_foo<T: Foo>(&mut self, foo: Box<T>) -> Handle<T> {
// ...
}
// In a real-world API this would return an `Option`.
pub fn return_foo<T: Foo>(&mut self, handle: Handle<T>) -> Box<T> {
// ...
}
}
I came up with this (Rust Playground) but not sure if there's a better way or if it's safe even. What do you think of that approach?
When writing callbacks for generic interfaces, it can be useful for them to define their own local data which they are responsible for creating and accessing.
In C I would just use a void pointer, C-like example:
struct SomeTool {
int type;
void *custom_data;
};
void invoke(SomeTool *tool) {
StructOnlyForThisTool *data = malloc(sizeof(*data));
/* ... fill in the data ... */
tool.custom_data = custom_data;
}
void execute(SomeTool *tool) {
StructOnlyForThisTool *data = tool.custom_data;
if (data.foo_bar) { /* do something */ }
}
When writing something similar in Rust, replacing void * with Option<Box<Any>>, however I'm finding that accessing the data is unreasonably verbose, eg:
struct SomeTool {
type: i32,
custom_data: Option<Box<Any>>,
};
fn invoke(tool: &mut SomeTool) {
let data = StructOnlyForThisTool { /* my custom data */ }
/* ... fill in the data ... */
tool.custom_data = Some(Box::new(custom_data));
}
fn execute(tool: &mut SomeTool) {
let data = tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap();
if data.foo_bar { /* do something */ }
}
There is one line here which I'd like to be able to write in a more compact way:
tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap()
tool.custom_data.as_ref().unwrap().downcast_mut::<StructOnlyForThisTool>().unwrap()
While each method makes sense on its own, in practice it's not something I'd want to write throughout a code-base, and not something I'm going to want to type out often or remember easily.
By convention, the uses of unwrap here aren't dangerous because:
While only some tools define custom data, the ones that do always define it.
When the data is set, by convention the tool only ever sets its own data. So there is no chance of having the wrong data.
Any time these conventions aren't followed, its a bug and should panic.
Given these conventions, and assuming accessing custom-data from a tool is something that's done often - what would be a good way to simplify this expression?
Some possible options:
Remove the Option, just use Box<Any> with Box::new(()) representing None so access can be simplified a little.
Use a macro or function to hide verbosity - passing in the Option<Box<Any>>: will work of course, but prefer not - would use as a last resort.
Add a trait to Option<Box<Any>> which exposes a method such as tool.custom_data.unwrap_box::<StructOnlyForThisTool>() with matching unwrap_box_mut.
Update 1): since asking this question a point I didn't include seems relevant.
There may be multiple callback functions like execute which must all be able to access the custom_data. At the time I didn't think this was important to point out.
Update 2): Wrapping this in a function which takes tool isn't practical, since the borrow checker then prevents further access to members of tool until the cast variable goes out of scope, I found the only reliable way to do this was to write a macro.
If the implementation really only has a single method with a name like execute, that is a strong indication to consider using a closure to capture the implementation data. SomeTool can incorporate an arbitrary callable in a type-erased manner using a boxed FnMut, as shown in this answer. execute() then boils down to invoking the closure stored in the struct field implementation closure using (self.impl_)(). For a more general approach, that will also work when you have more methods on the implementation, read on.
An idiomatic and type-safe equivalent of the type+dataptr C pattern is to store the implementation type and pointer to data together as a trait object. The SomeTool struct can contain a single field, a boxed SomeToolImpl trait object, where the trait specifies tool-specific methods such as execute. This has the following characteristics:
You no longer need an explicit type field because the run-time type information is incorporated in the trait object.
Each tool's implementation of the trait methods can access its own data in a type-safe manner without casts or unwraps. This is because the trait object's vtable automatically invokes the correct function for the correct trait implementation, and it is a compile-time error to try to invoke a different one.
The "fat pointer" representation of the trait object has the same performance characteristics as the type+dataptr pair - for example, the size of SomeTool will be two pointers, and accessing the implementation data will still involve a single pointer dereference.
Here is an example implementation:
struct SomeTool {
impl_: Box<SomeToolImpl>,
}
impl SomeTool {
fn execute(&mut self) {
self.impl_.execute();
}
}
trait SomeToolImpl {
fn execute(&mut self);
}
struct SpecificTool1 {
foo_bar: bool
}
impl SpecificTool1 {
pub fn new(foo_bar: bool) -> SomeTool {
let my_data = SpecificTool1 { foo_bar: foo_bar };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool1 {
fn execute(&mut self) {
println!("I am {}", self.foo_bar);
}
}
struct SpecificTool2 {
num: u64
}
impl SpecificTool2 {
pub fn new(num: u64) -> SomeTool {
let my_data = SpecificTool2 { num: num };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool2 {
fn execute(&mut self) {
println!("I am {}", self.num);
}
}
pub fn main() {
let mut tool1: SomeTool = SpecificTool1::new(true);
let mut tool2: SomeTool = SpecificTool2::new(42);
tool1.execute();
tool2.execute();
}
Note that, in this design, it doesn't make sense to make implementation an Option because we always associate the tool type with the implementation. While it is perfectly valid to have an implementation without data, it must always have a type associated with it.
Suppose I have a rust trait that contains a function that does not take a &self parameter. Is there a way for me to call this function based on a generic type parameter of the concrete type that implements that trait? For example, in the get_type_id function below, how do I successfully call the type_id() function for the CustomType trait?
pub trait TypeTrait {
fn type_id() -> u16;
}
pub struct CustomType {
// fields...
}
impl TypeTrait for CustomType {
fn type_id() -> u16 { 0 }
}
pub fn get_type_id<T : TypeTrait>() {
// how?
}
Thanks!
As Aatch mentioned, this isn't currently possible. A workaround is to use a dummy parameter to specify the type of Self:
pub trait TypeTrait {
fn type_id(_: Option<Self>) -> u16;
}
pub struct CustomType {
// fields...
}
impl TypeTrait for CustomType {
fn type_id(_: Option<CustomType>) -> u16 { 0 }
}
pub fn get_type_id<T : TypeTrait>() {
let type_id = TypeTrait::type_id(None::<T>);
}
Unfortunately, this isn't currently possible. It used to be, based on a implementation detail, however that was removed in favor of eventually implementing a proper way of doing this.
When it is eventually implemented, it may end up looking something like this: TypeTrait::<for T>::type_id(), however there is, currently, no syntax set in stone.
This is a known case and one that is fully intended to be supported, it is just unfortunate that it currently is not possible.
The full discussion about this topic (called associated methods) is here: https://github.com/mozilla/rust/issues/6894 and here: https://github.com/mozilla/rust/issues/8888