Is it possible to assign the return value of different functions that return different structs (that implement a common trait) to a single variable? - rust

Let's say I have something like this:
trait SomeTrait {}
struct One;
impl SomeTrait for One {}
struct Two;
impl SomeTrait for Two {}
fn return_one() -> One {
One
}
fn return_two() -> Two {
Two
}
Somewhere else, I want to essentially do:
fn do_stuff(op: Operation) {
let result = match op {
Operation::OpOne => return_one(),
Operation::OpTwo => return_two(),
};
}
That of course doesn't compile, as those two return_*() functions return distinct types. I've tried:
Declaring result as dyn SomeTrait (still error: mismatched types)
Casting the return values, e.g. return_one() as dyn SomeTrait (error: cast to unsized type: One as dyn SomeTrait)
Making Sized a supertrait of SomeTrait (this won't work for me in this particular case as I don't have control over the real-world version of SomeTrait, but it doesn't compile anyway: error: the trait SomeTrait cannot be made into an object
Things I think would work but don't want to or can't do:
Boxing values on return, e.g. Box::new(return_one()) as dyn Box<SomeTrait> (having to move the values into a box, and thus off the stack, seems excessive)
Having return_one() and return_two() instead return impl SomeTrait (this would allow me to accidentally return Two from return_one(), for example, and I want to use the type system to prevent that)
Wrapping with an enum: I don't want the functions to return a wrapper enum, because then we have the same problem as the previous bullet point. I could wrap the return values in an enum at the call site, and that could work, but let's say there's some function on SomeTrait that I want to call at the end; it seems like a lot of extra boilerplate to then unwrap the enum and call that function for each inner type. If I were to do that, I might as well just copy-paste the trait function call to each match arm.
I found a few crates on crates.io that claim to be able to do this, but AFAICT they all require implementing a trait on the types, which are foreign types for me, so I can't do that.
Is it possible to make this work?

A possible option is to do the following
fn do_stuff(op: Operation) {
let (one, two);
let _result: &dyn SomeTrait = match op {
Operation::OpOne => {one = return_one(); &one},
Operation::OpTwo => {two = return_two(); &two},
};
}
You can also use &mut dyn SomeTrait instead if you need to borrow it mutably.
This is somewhat verbose, but if you find yourself doing it a lot, a macro
that declares the anonymous variables, assigns them and returns a reference might help.
Another solution could be to use the auto_enums crate, which automaticaly creates the enum and implements the trait for it, the downside is that it only supports certain traits, (mostly in std, I believe) and that for this specific use case it requires nightly, or putting the match in a separate function.
I'm not sure I can link a specific part of the docs, but if you scroll down to "#Rust Nightly", you'll see your specific use of it, something like as follows
use auto_enums::auto_enum;
fn do_stuff(op: Operation) {
#[auto_enum(SomeTrait)]
let _result = match op {
Operation::OpOne => return_one(),
Operation::OpTwo => return_two(),
};
}
Although keep in mind this only works if auto_enums supports SomeTrait.

Related

Is there a way to reference an anonymous type as a struct member?

Consider the following Rust code:
use std::future::Future;
use std::pin::Pin;
fn main() {
let mut v: Vec<_> = Vec::new();
for _ in 1..10 {
v.push(wrap_future(Box::pin(async {})));
}
}
fn wrap_future<T>(a: Pin<Box<dyn Future<Output=T>>>) -> impl Future<Output=T> {
async {
println!("doing stuff before awaiting");
let result=a.await;
println!("doing stuff after awaiting");
result
}
}
As you can see, the futures I'm putting into the Vec don't need to be boxed, since they are all the same type and the compiler can infer what that type is.
I would like to create a struct that has this Vec<...> type as one of its members, so that I could add a line at the end of main():
let thing = MyStruct {myvec: v};
without any additional overhead (i.e. boxing).
Type inference and impl Trait syntax aren't allowed on struct members, and since the future type returned by an async block exists entirely within the compiler and is exclusive to that exact async block, there's no way to reference it by name. It seems to me that what I want to do is impossible. Is it? If so, will it become possible in a future version of Rust?
I am aware that it would be easy to sidestep this problem by simply boxing all the futures in the Vec as I did the argument to wrap_future() but I'd prefer not to do this if I can avoid it.
I am well aware that doing this would mean that there could be only one async block in my entire codebase whose result values could possibly be added to such a Vec, and thus that there could be only one function in my entire codebase that could create values that could possibly be pushed to it. I am okay with this limitation.
Nevermind, I'm stupid. I forgot that structs could have type parameters.
struct MyStruct<F> where F: Future<Output=()> {
myvec: Vec<F>,
}

Is it possible to return a `Keys<'_, K, V>` iterator as an more generic iterator of `&K`?

I would to write a method that returns self.hash_map.keys() while hiding from the caller the concrete type Keys<'_, K, V>.
A downside of the Keys return type is that it exposes to the caller that the K elements are coming from a HashMap. Intuitively, it seems as though it shouldn't be necessary to expose this information.
What is the cheapest way (with respect to CPU/allocations) to return an iterator of key references?
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Or does it require an invocation in the function body? Is some transformation necessary?
Both of the options you speculated about are possible.
The simplest option is to use the impl type syntax, which is an “existential” type: “I am going to return a value which implements Iterator but I'm not telling you what the concrete type is”. In this case, the compiler knows what the type is (so the compiled code is exactly the same as if it wasn't hidden), but the user of your method cannot rely on anything but the specified trait, so you aren't leaking implementation details.
impl MyType {
fn keys(&self) -> impl Iterator<Item = &MyKeyType> {
self.hash_map.keys()
}
}
(Note that this resembles but is not the same as dyn Iterator; when you use dyn, you're using runtime dispatch and the same function can return different concrete types from different calls to it. With impl, the type is static, just hidden, and there is no overhead.)
The disadvantage of this option is that the type is entirely unnameable; for example, nobody can write a structure that holds your keys() iterator except by making it generic over all Iterators. (This is rarely a problem for iterators in particular, since iterator wrappers are usually generic anyway.)
Also, if your iterator implements any additional traits you want to allow the caller to use, like Debug or ExactSizeIterator, then you need to add them to the impl type or they won't be visible.
Another option is to wrap the iterator in your own struct. This allows you to hide the implementation type while still allowing callers to refer to it by name, so it's the most flexible. The disadvantage of this option is that you have to explicitly implement Iterator (and any other traits) for the wrapper:
impl MyType {
fn keys(&self) -> MyKeyIterator<'_> {
MyKeyIterator(self.hash_map.keys())
}
}
#[derive(Clone, Debug)]
struct MyKeyIterator<'a>(Keys<'a, MyKeyType, MyValueType>);
impl<'a> Iterator for MyKeyIterator<'a> {
type Item = &'a MyKeyType;
fn next(&mut self) -> Option<&'a MyKeyType> {
self.0.next()
}
}
Rust Playground link with supporting code
This wrapper should not add any performance cost (when compiled with optimization), except that by default the wrapper method will not be inlined if called from another crate. If you're writing a library and this method is performance-sensitive, you can either enable link-time optimization (LTO) in the build, or add #[inline] to the next method (which enables cross-crate inlining). Of course, don't do any such tweaking without checking whether it makes a difference to actual performance; otherwise you're just increasing compile time (and instruction cache thrashing).
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Yes! You can return an impl Trait to indicate that you're returning a type that implements Trait but doesn't expose the concrete type:
fn keys(&self) -> impl Iterator<Item = &K> {
self.hash_map.keys()
}
See it working on the playground.

How can I implement `From<some trait's associated type>?`

I'm writing a new crate, and I want it to be useable with any implementation of a trait (defined in another crate). The trait looks something like this:
pub trait Trait {
type Error;
...
}
I have my own Error type, but sometimes I just want to forward the underlying error unmodified. My instinct is to define a type like this:
pub enum Error<T: Trait> {
TraitError(T::Error),
...
}
This is similar to the pattern encouraged by thiserror, and appears to be idiomatic. It works fine, but I also want to use ? in my implementation, so I need to implement From:
impl<T: Trait> From<T::Error> for Error<T> {
fn from(e: T::Error) -> Self { Self::TraitError(e) }
}
That fails, because it conflicts with impl<T> core::convert::From<T> for T. I think I understand why — some other implementor of Trait could set type Error = my_crate::Error such that both impls would apply — but how else can I achieve similar semantics?
I've looked at a few other crates, and they seem to handle this by making their Error (or equivalent) generic over the error type itself, rather than the trait implementation. That works, of course, but:
until we have inherent associated types, it's much more verbose. My T actually implements multiple traits, each with their own Error types, so I'd now have to return types like Result<..., Error<<T as TraitA>::Error, <T as TraitB>::Error>> etc;
it's arguably less expressive (because the relationship to Trait is lost).
Is making my Error generic over the individual types the best (most idiomatic) option today?
Instead of implementing From for your Error enum, consider instead using Result::map_err in combination with ? to specify which variant to return.
This works even for enums generic over a type using associated types, as such:
trait TraitA {
type Error;
fn do_stuff(&self) -> Result<(), Self::Error>;
}
trait TraitB {
type Error;
fn do_other_stuff(&self) -> Result<(), Self::Error>;
}
enum Error<T: TraitA + TraitB> {
DoStuff(<T as TraitA>::Error),
DoOtherStuff(<T as TraitB>::Error),
}
fn my_function<T: TraitA + TraitB>(t: T) -> Result<(), Error<T>> {
t.do_stuff().map_err(Error::DoStuff)?;
t.do_other_stuff().map_err(Error::DoOtherStuff)?;
Ok(())
}
On the playground
Here the important bits are that Error has no From implementations (aside from blanket ones), and that the variant is specified using map_err. This works as Error::DoStuff can be interpreted as a fn(<T as TraitA>::Error) -> Error when passed to map_err. Same happens with Error::DoOtherStuff.
This approach is scaleable with however many variants Error has and whether or not they are the same type. It might also be clearer to someone reading the function, as they can figure out that a certain error comes from a certain place without needing to check From implementations and where the type being converted from appears in the function.

How to use PalletB to save record from PalletA without PalletA knowing anything about internals of the saving in substrate and rust

I'd like to save a record from PalletA in PalletB by simply passing the raw data and waiting for the return.
I have tried following:
// ./PalletB/lib.rs
pub trait PutInStorage {
fn put_rule_in_storage(value: u32);
}
impl<T: Trait> PutInStorage for Module<T> {
fn put_rule_in_storage(value: u32) {
SimpleCounter::put(value);
}
}
then in
// ./PalletA/lib.rs
use palletB::{PutInStorage, Trait as PalletBTrait};
///The pallet's configuration trait.
pub trait Trait: system::Trait + PalletBTrait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type ExternalStorage: PutInStorage;
}
then I added the definition to the runtime like this:
// ./runtime/lib.rs
// near the construct_runtime macro
impl palletA::Trait for Runtime {
type Event = Event;
type ExternalStorage = palletB::Module<Runtime>;
}
So far this passes the check but not the test. The test configuration for the trait is like this:
use palletB::{PutInStorage, Trait as PalletBTrait};
impl Trait for Test {
type Event = ();
type ExternalStorage = PutInStorage;
}
and this fails with the:
type ExternalRulesStorage = PutInStorage;
^^^^^^^^^^^^ help: use `dyn`: `dyn PutInStorage`
impl Trait for Test
------------------- in this `impl` item
type Event = ();
type ExternalRulesStorage = PutInStorage;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
type ExternalRulesStorage = PutInStorage;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `pallet_rules::PutInStorage` cannot be made into an object
I tried all the suggestions the Rust compiler gives me but without any luck. Before someone asks why do I need this in my test, it's because a dispatchable fn in decl_module! checks does a certain record exists before starts processing and saving its own records. It is dependent on record to exist.
To make the compiler happy, your test config must also have an instance of PalletB, or anything else that implements PutInStorage.
Similar to what you've done already in ./runtime/lib.rs:
impl Trait for Test {
type Event = ();
type ExternalStorage = palletB::Module<Test>;
}
Note that now struct Test is the one playing the role of Runtime. I think this is the only thing that you are missing.
That being said, you seem to be on the wrong track in the overall design.
PalletA already depends on PalletB. Given that you also have a trait to link the two PutInStorage, this is not a good design. Generally, you should try and always chose one of the following:
Two pallets will depend on one another. In this case you don't need traits. If one needs to put something to the storage of the other one, you just do it directly. In your example, I assume that PalletB has a storage item called pub Foo: u32 in decl_storage and PutInStorage writes to this. If this is the case, there's no need for a trait. From PalletA you can just say: palletB::Foo::put(value).
Note that this approach should be chosen with care, otherwise you might end up with a lot of pallets depending on one another which is not good.
You decide that your pallets do NOT depend on one another, in which case you use some trait like PutInStorage. Your code seem to be aligned with this approach, except you define PalletA's Trait as pub trait Trait: system::Trait. There's no need to depend on PalletB here, and of course you can wipe it from Cargo.toml as well.

How to Box a trait that has associated types?

I'm very new to Rust so I may have terminology confused.
I want to use the hashes crates to do some hashing and I want to dynamically pick which algorithm (sha256, sha512, etc.) to use at runtime.
I'd like to write something like this:
let hasher = match "one of the algorithms" {
"sha256" => Box::new(Sha256::new()) as Box<Digest>,
"sha512" => Box::new(Sha512::new()) as Box<Digest>
// etc...
};
I sort of get that that doesn't work because the associated types required by Digest aren't specified. If I attempt to fill them in:
"sha256" => Box::new(Sha256::new()) as Box<Digest<<OutputSize = U32, BlockSize = U64>>>,
I'm left with an error: the trait 'digest::Digest' cannot be made into an object. I think this approach will fail anyway because match will be returning slightly different types in cases where different algorithms have different associated types.
Am I missing something obvious? How can I dynamically create an instance of something that implements a trait and then hold on to that thing and use it through the trait interface?
The message refers to object safety (longer article). The Digest trait has two incompatibilities:
It uses associated types (this can be worked around by explicitly setting all type parameters to values compatible for all Digest objects).
It has a method (fn result(self) -> …) taking self by value. You won't be able to call it, which ruins usability of this trait.
Once a trait object is created, information about its subtype-specific features such as memory layout or associated types is erased. All calls to the trait object's methods are done via a vtable pointer. This means they all must be compatible, and Rust can't allow you to call any methods that could vary in these aspects.
A workaround is to create your custom wrapper trait/adapter that is object-compatible. I'm not sure if that's the best implementation, but it does work:
trait Digest {
type Assoc;
fn result(self);
}
struct Sha;
impl Digest for Sha {
type Assoc = u8;
fn result(self) {}
}
///////////////////////////////////////////
trait MyWrapper {
fn result(&mut self); // can't be self/Sized
}
impl<T: Digest> MyWrapper for Option<T> {
fn result(&mut self) {
// Option::take() gives owned from non-owned
self.take().unwrap().result()
}
}
fn main() {
let mut digest: Box<MyWrapper> = Box::new(Some(Sha));
digest.result();
}

Resources