I'm trying to use the AES crate, which offers three algorithms: AES128, AES192 and AES256. I'm trying to create a struct that can create the correct algorithm by detecting the key size, and save it to later use.
I see they all implement the BlockEncrypt (I only need encryption) trait, but when I try to make a field in the struct with this type, even when supplying the size, i get an "the trait BlockEncrypt cannot be made into an object. the trait cannot be made into an object because it requires Self: Sized " error.
pub struct MyStruct<'a, T: Sized> {
ciph: Box< dyn BlockEncrypt<BlockSize = T>>,
}
Traits that have Sized as a supertype aren't "object safe" which means dynamic dispatch using dyn isn't possible on these kinds of traits.
You can accomplish this relatively tersely by using an enum for each possible type you want to handle, combined with a macro that will neatly generate all the match arms required to dynamically dispatch on each enum possibility.
To make things simple for the sake of this explanation, I'm going to define a new trait (with the Sized supertype) so that this answer doesn't depend on a particular crate:
trait SampleTrait: Sized {
fn method_a(&self, foo: &str);
fn method_b(&self, foo: i32, bar: String) -> String;
}
Now we need at least two implementations of this trait to demonstrate the solution:
struct ImplA;
struct ImplB;
impl SampleTrait for ImplA {
fn method_a(&self, foo: &str) {
println!("<ImplA as SampleTrait>::method_a(): {:?}", foo);
}
fn method_b(&self, foo: i32, bar: String) -> String {
println!("<ImplA as SampleTrait>::method_b(): {:?} {:?}", foo, bar);
format!("from ImplA: {}", bar)
}
}
impl SampleTrait for ImplB {
fn method_a(&self, foo: &str) {
println!("<ImplB as SampleTrait>::method_a(): {:?}", foo);
}
fn method_b(&self, foo: i32, bar: String) -> String {
println!("<ImplB as SampleTrait>::method_b(): {:?} {:?}", foo, bar);
format!("from ImplB: {}", bar)
}
}
The enum just needs to least each possible type that you need to handle:
enum DynSampleTrait {
ImplA(ImplA),
ImplB(ImplB),
}
Now to dispatch on this, you'd have to do something like:
match value {
DynSampleTrait::ImplA(v) => v.method_a("something"),
DynSampleTrait::ImplB(v) => v.method_a("something"),
}
To get around having to repeat ourselves, let's declare a macro that will generate the whole match for us:
macro_rules! dyn_sample_trait_call {
(($m:expr) $v:ident => $code:block) => {
match $m {
DynSampleTrait::ImplA($v) => $code,
DynSampleTrait::ImplB($v) => $code,
}
}
}
Now we can use the macro like so:
dyn_sample_trait_call!((value) v => { v.method_a("something") })
value goes in the match expression, and v is the identifier that unwraps the inner value held by the enum possibility. These are separate so that you can match over a reference, should you not want to consume an owned DynSampleTrait:
dyn_sample_trait_call!((&value) v => { v.method_a("something") })
(Playground)
As #cdhowie mentioned, you can't create a trait object from a trait with a Sized bound. Instead, you can create an enum:
enum MyAlgorithm {
AES128(AES128),
AES192(AES192),
AES256(AES256),
}
Related
I have these two types:
pub struct A;
pub struct B;
The following trait:
pub trait MyTrait {}
and a free function:
fn do_something(mut element: impl MyTrait) { ... }
I want to, based on the real type behind the impl, do something different depending the type of the element that is coming in the current function call.
// pseudo-snippet
match element {
A => do something with A,
B => do something with B
}
Is possible in Rust, determine the type behind an impl statement? Or, at least, make some decision making workflow based (if-else branches, patter matching...) on that impl?
The way to do this is to add a method to MyTrait, which does the two different things you want, or which returns an enum that you can match.
pub trait MyTrait {
fn do_something(self);
}
There is no mechanism to directly branch on what a concrete type is in the way you mean. Technically you could achieve it with std::any::TypeId, but you shouldn't — because you're defeating static checking, making it possible for an error in your program (not handling the right set of types) to be delayed until run time. Instead, by putting the functionality into MyTrait, your program has the property that if some type implements MyTrait, there is necessarily a definition of what do_something should do with that trait.
There is no syntax for match on types. The only way to do any kind of "downcasting" is via the Any trait. You can use it with an impl MyTrait if it also constrained to be 'static:
use std::any::Any;
pub struct A;
pub struct B;
pub struct C;
pub trait MyTrait {}
impl MyTrait for A {}
impl MyTrait for B {}
impl MyTrait for C {}
fn do_something(mut element: impl MyTrait + 'static) {
let element = &mut element as &mut dyn Any;
if let Some(a) = element.downcast_mut::<A>() {
// do something with `a`
println!("is A");
} else if let Some(b) = element.downcast_mut::<B>() {
// do something with `b`
println!("is B");
} else {
// do something with `element`
println!("is unknown");
}
}
fn main() {
do_something(A);
do_something(B);
do_something(C);
}
is A
is B
is unknown
The two caveats with this code are 1) the 'static constraint, meaning you can't use this with traits or implementations with non-static generic lifetimes; and 2) you can't get an owned version of A or B (the type of a is &mut A in the above code).
This type of code pattern is very un-idiomatic. If you want to have trait instances exhibit different behavior based on their implementations, you should have an associated method for it:
pub trait MyTrait {
fn do_something(self)
}
Or if you want to implement behavior on a closed-set of types with shared behavior, you should instead use an enum:
enum MyEnum {
A(A),
B(B),
}
fn do_something(element: MyEnum) {
match element {
MyEnum::A(a) => { println!("is A"); },
MyEnum::B(b) => { println!("is B"); },
}
}
With either of these methods, you don't have the same caveats of trying to circumvent the polymorphic trait system.
See also (none address the impl Trait case, but they pretty much all say the same thing):
How to match trait implementors
How do you match a trait in rust?
How to match on data type in Rust?
Recently I've been working on learning advanced Rust. As part of that, I'm learning to use dynamic dispatch.
In my tinkering I've run across a bit of an issue. For some reason, I can't seem to access fields of structs which have been assigned to variables using Boxes and dynamic dispatch. For example,
fn main() {
let z: Box<dyn S>;
z = Box::new(A::new());
println!("{}", z.val);
}
trait S {
fn new () -> Self where Self: Sized;
}
struct A {
val: i32,
}
impl S for A {
fn new () -> A {
A {val: 1}
}
}
struct B {
val: i32
}
impl S for B {
fn new() -> B {
B {val:2}
}
}
yields the error message "error[E0609]: no field val on type Box<dyn S>"
Is there any way to access such fields, or do I need to kluge together a workaround?
It is easy to understand why this does not work if you understand what a trait object is. When a method returns a dyn Trait it does not return an instance of any struct. Instead it returns a lookup table, which tells the caller where to find its methods. So the caller can access methods without knowing anything about underlying struct itself.
So if the caller does not have access to the struct itself it's clear it cannot access its fields.
There are two ways to implement what you are trying to do:
Use methods of the trait to access required fields. This is a good option if the list of possible implementors of the trait is not predetermined, and if the trait itself is not too complex (as traits that can be represented as trait objects have some limitations). Not though that trait objects come with some runtime overhead (using lookup tables is slower then direct method access).
Use enum. If you know complete list of options that the method can return, it's the best and the most Rust'y solution.
enum S {
A { val: i32, anotherAval: u32 },
B { val: i32, anotherBval: f32 },
}
impl S {
fn val(&self) -> i32 {
match self {
S::A => A.val,
S::B => B.val,
}
}
}
I am trying to implement the following trait and struct:
pub trait Funct {
fn arity(&self) -> u32;
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct FunctionLiteral<T: Funct> {
pub function: T,
pub args: Vec< FunctionLiteral<T> >
}
pub enum Foo {
Foo
}
impl Funct for Foo {
fn arity(&self) -> u32 {0}
}
pub enum Bar {
Bar
}
impl Funct for Bar {
fn arity(&self) -> u32 {0}
}
fn main() {
let baz = FunctionLiteral{
function: Foo::Foo,
args: vec![FunctionLiteral{
function: Bar::Bar,
args: vec![]
}]
};
}
I can set it up the way I have for the generic type T to be of trait Funct, but I don't necessarily want T to be of the same type.
Here, compiling the code gives the following error:
error[E0308]: mismatched types
--> foo.rs:31:23
|
31 | function: Bar::Bar,
| ^^^^^^^^ expected enum `Foo`, found enum `Bar`
error: aborting due to previous error
Is it possible to set up FunctionLiteral such that I can have different types for function and the items of args, while forcing both of them to be of type Funct?
The problem
When you do:
Structure<T: Trait>{
inner: T,
many: Vec<T>
}
You are telling the compiler to create a specialized instance for each different T. So if you have Foo and Bar both implementing Trait, then the compiler will generate two different representations, with two different sizes:
struct Foo(u8);
impl Trait for Foo{
// impl goes here
}
struct Bar(u64);
impl Trait for Bar{
// impl goes here
}
Then the compiler will generate something like:
Structure<Foo>{
inner: Foo,
many: Vec<Foo>
}
// and
Structure<Bar>{
inner: Bar,
many: Vec<Bar>
}
Obviously you cannot put Foo instances into Bar as they are different types and have different sizes.
The solution
You need to Box<> your Funct types in order to make them the same size (i.e. pointer sized). By putting them behind a (smart) pointer you are essentially erasing their types:
let a: Box<dyn Trait> = Box::new(Foo(0));
let b: Box<dyn Trait> = Box::new(Bar(0));
Now both a and b have the same size (the size of a pointer) and have the same type - Box<dyn Trait>. So now you can do:
struct Structure{ // no longer generic!
inner: Box<dyn Trait>, // can hold any `Box<dyn Trait>`
many: Vec<Box<dyn Trait>> // can hold any `Box<dyn Trait>`
}
The downside of this approach is that it requires heap allocation and that it looses the exact type of a and b. Youno longer know if a is Foo or Bar or something else.
Instead of Box you can use any other smart pointer, such as Rc or Arc if you need its functionality.
Another option
Another option is to make Foo and Bar the same size and type. This can be done by wrapping them in an enum:
enum Holder{
Foo(Foo),
Bar(Bar), // any other types go here in their own variants
}
Then your structure will look like:
struct Structure{ // no longer generic!
inner: Holder, // can hold any Holder variant`
many: Vec<Holder> // can hold any Holder variant`
}
The downside is that you have to either implement a delegation like:
impl Trait for Holder{
fn some_method(&self){
match self{
Holder::Foo(foo) => foo.some_method(),
Holder::Bar(bar) => bar.some_method(),
}
}
}
Or match everywhere you want to use the object. Also now your Holder enum will be the size of max(sizeof(Foo), sizeof(Bar))
On the plus side:
you still know the actual type - it's not erased
no heap allocation
I think I misunderstand the purpose of enums.
I am looking to implement the XML DOM for practice. DOM nodes all have a list of methods associated with them, but some nodes (such as the document node) have additional methods not available on other nodes.
I was hoping to have all the main methods set up on the enum and then the variants would have the methods specific to them, but it seems like the method variants are not callable on an enum, but continue to be callable on the parameter.
#[derive(Clone, Debug, Copy)]
enum MyEnum {
MyType(MyType),
}
impl MyEnum {
pub fn do_a_thing(&self) -> i32 {
// Some code here
return 1;
}
}
#[derive(Clone, Debug, Copy)]
pub struct MyType {}
impl MyType {
pub fn do_another_thing(self) -> i32 {
// Some more code here
return 2;
}
}
fn main() {
let x = MyEnum::MyType(MyType {});
println!("{:?}", x);
println!("I can call do_a_thing, it is {}", x.do_a_thing());
println!(
"Why can't I call do_another_thing? {}",
x.do_another_thing()
);
}
This works fine, but my gut tells me I am going the wrong way about things:
impl MyEnum {
pub fn do_a_thing(&self) -> i32 {
// Some code here
return 1;
}
pub fn do_another_thing(self) -> i32 {
match self {
MyEnum::MyType(MT) => MT.do_another_thing(),
}
}
}
How should I implement this?
You are using an enum, so it's likely you're going to add more variants at some point (otherwise, why would you use enums, right?).
When Rust sees a value whose type is an enum, at compile-time it assumes the value could be any of the enum's variants, so it won't let you call a method on any variant until you check its type.
To do that is easy, as you've already shown in your question:
match self {
MyEnum::MyType(t) => t.do_another_thing()
}
You might be interested to know there's also if let in case you only want to check a single variant:
let x = MyEnum::MyType(MyType{});
if let MyEnum::MyType(t) = x {
t.do_another_thing();
}
You might want to have methods that are callable on ALL variants as well, in which case you'll need to either implement the method directly on the enum as you've done above, or use a trait that is implemented by the enum, which perhaps makes your code look more polymorphic as in most other languages (where there would be an interface for the nodes).
That will make your code in main work.
It might look like this:
#[derive(Clone, Debug, Copy)]
enum MyEnum {
MyType(MyType)
}
impl MyEnum {
pub fn do_a_thing(&self) -> i32{
// Some code here
return 1
}
}
#[derive(Clone, Debug, Copy)]
pub struct MyType {
}
trait MyTrait {
fn do_another_thing(&self) -> i32;
}
impl MyTrait for MyEnum {
fn do_another_thing(&self) -> i32 {
// Some more code here
return 2
}
}
fn main() {
let x = MyEnum::MyType(MyType{});
println!("{:?}",x);
println!("I can call do_a_thing, it is {}", x.do_a_thing());
println!("Why can't I call do_another_thing? {}", x.do_another_thing());
}
Is there a way to cast from one trait to another?
I have the traits Foo and Bar and a Vec<Box<dyn Foo>>. I know some of the items in the Vec implement the Bar trait, but is there any way I could target them?
I don't understand if this is possible or not.
trait Foo {
fn do_foo(&self);
}
trait Bar {
fn do_bar(&self);
}
struct SomeFoo;
impl Foo for SomeFoo {
fn do_foo(&self) {
println!("doing foo");
}
}
struct SomeFooBar;
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
}
impl Bar for SomeFooBar {
fn do_bar(&self) {
println!("doing bar");
}
}
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
// if let Some(val) = foo.downcast_whatever::<Bar>() {
// val.bar();
// }
}
}
[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8b637bddc4fc923ce705e84ad1d783d4)
No. There is no way to cast between two unrelated traits. To understand why, we have to understand how trait objects are implemented. To start with, let's look at TraitObject.
TraitObject is a reflection of how trait objects are actually implemented. They are composed of two pointers: data and vtable. The data value is just a reference to the original object:
#![feature(raw)]
use std::{mem, raw};
trait Foo {}
impl Foo for u8 {}
fn main() {
let i = 42u8;
let t = &i as &dyn Foo;
let to: raw::TraitObject = unsafe { mem::transmute(t) };
println!("{:p}", to.data);
println!("{:p}", &i);
}
vtable points to a table of function pointers. This table contains references to each implemented trait method, ordered by some compiler-internal manner.
For this hypothetical input
trait Foo {
fn one(&self);
}
impl Foo for u8 {
fn one(&self) { println!("u8!") }
}
The table is something like this pseudocode
const FOO_U8_VTABLE: _ = [impl_of_foo_u8_one];
A trait object knows a pointer to the data and a pointer to a list of methods that make up that trait. From this information, there is no way to get any other piece of data.
Well, almost no way. As you might guess, you can add a method to the vtable that returns a different trait object. In computer science, all problems can be solved by adding another layer of indirection (except too many layers of indirection).
See also:
Why doesn't Rust support trait object upcasting?
But couldn't the data part of the TraitObject be transmuted to the struct
Not safely, no. A trait object contains no information about the original type. All it has is a raw pointer containing an address in memory. You could unsafely transmute it to a &Foo or a &u8 or a &(), but neither the compiler nor the runtime data have any idea what concrete type it originally was.
The Any trait actually does this by also tracking the type ID of the original struct. If you ask for a reference to the correct type, the trait will transmute the data pointer for you.
Is there a pattern other than the one I described with my FooOrBar trait to handle such cases where we need to iterate over a bunch of trait objects but treat some of them slightly different?
If you own these traits, then you can add as_foo to the Bar trait and vice versa.
You could create an enum that holds either a Box<dyn Foo> or a Box<dyn Bar> and then pattern match.
You could move the body of bar into the body of foo for that implementation.
You could implement a third trait Quux where calling <FooStruct as Quux>::quux calls Foo::foo and calling <BarStruct as Quux>::quux calls Bar::foo followed by Bar::bar.
so... I don't think this is exactly what you want, but it's the closest I can get.
// first indirection: trait objects
let sf: Box<Foo> = Box::new(SomeFoo);
let sb: Box<Bar> = Box::new(SomeFooBar);
// second level of indirection: Box<Any> (Any in this case
// is the first Box with the trait object, so we have a Box<Box<Foo>>
let foos: Vec<Box<Any>> = vec![Box::new(sf), Box::new(sb)];
// downcasting to the trait objects
for foo in foos {
match foo.downcast::<Box<Foo>>() {
Ok(f) => f.do_foo(),
Err(other) => {
if let Ok(bar) = other.downcast::<Box<Bar>>() {
bar.do_bar();
}
}
}
}
note that we can call SomeFooBar as a Box<Bar> only because we stored it as a Box<Bar> in the first place. So this is still not what you want (SomeFooBar is a Foo too, but you can't convert it to a Box<Foo> any longer, so we're not really converting one trait to the other)
The short answer is: there is extremely limited support for downcasting at the moment in the language.
The long answer is that being able to downcast is not seen as high-priority for both technical and philosophical reasons:
from a technical stand-point, there are workarounds for most if not all situations
from a philosophical stand-point, downcasting leads to more brittle software (as you unexpectedly start relying on implementation details)
There have been multiple proposals, and I myself participated, but for now none has been selected and it is unclear whether Rust will ever get downcasting or if it does what its limitations will be.
In the mean time, you have essentially two workarounds:
Use TypeId: each type has an associated TypeId value which can be queried, then you can build a type-erased container such as Any and query whether the type it holds is a specific X. Behind the scenes Any will simply check the TypeId of this X against the TypeId of the value stored.
Create a specific trait, as you did.
The latter is more open-ended, and notably can be used with traits, whereas the former is limited to concrete types.
Here is what I did.
I added an as_bar method to the Foo trait that returns an Option<&Bar>. I gave the trait a default implementation to return None so that there is little to no inconvenience for Foo implementers that don't bother about Bar.
trait Foo {
fn do_foo(&self);
fn as_bar(&self) -> Option<&dyn Bar> {
None
}
}
I overwrite that method for the SomeFooBar struct which implements both Foo and Bar to return Some(self):
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
fn as_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
}
Which makes the calling code look pretty much the way I want it to look.
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
if let Some(bar) = foo.as_bar() {
bar.do_bar();
}
}
}
Playground
I would love to see Rust improve on this part in the future, but it's a solution I can totally live with for my case.
The only solution that I found originally is to introduce a third trait FooOrBar with explicit converter methods and implement that for both types. It doesn't feel like the right tool for the job though.
trait FooOrBar {
fn to_bar(&self) -> Option<&dyn Bar>;
fn to_foo(&self) -> Option<&dyn Foo>;
}
impl FooOrBar for SomeFooBar {
fn to_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
fn to_foo(&self) -> Option<&dyn Foo> {
None
}
}
impl FooOrBar for SomeFoo {
fn to_bar(&self) -> Option<&dyn Bar> {
None
}
fn to_foo(&self) -> Option<&dyn Foo> {
Some(self)
}
}
fn main() {
let foos: Vec<Box<dyn FooOrBar>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.to_foo().map(|foo| foo.do_foo());
foo.to_bar().map(|foo| foo.do_bar());
}
}