Can traits be used on enum types? - enums

I read through the trait documentation and found a neat definition for using traits on structs. Is it possible to use traits on enum types? I have seen answers that say no, but they are 3 years old and don't quite do what I'm trying to do.
I tried to do this:
#[derive(Debug, Copy, Clone)]
pub enum SceneType {
Cutscene,
Game,
Menu,
Pause,
Credits,
Exit,
}
//We want to guarantee every SceneType can be played statically
trait Playable {
fn play();
}
impl Playable for SceneType::Cutscene {
fn play() {}
}
error[E0573]: expected type, found variant `SceneType::Cutscene`
--> src/main.rs:16:19
|
16 | impl Playable for SceneType::Cutscene {
| ^^^^^^^^^^^^^^^^^^^
| |
| not a type
| help: you can try using the variant's enum: `SceneType`
I don't understand this error because the enum it references is in the same file. If I really can't use traits on enum variants, is there any way I can guarantee any enum trait must implement certain methods?

Can traits be used on enum types?
Yes. In fact, you already have multiple traits defined for your enum; the traits Debug, Copy and Clone:
#[derive(Debug, Copy, Clone)]
pub enum SceneType
The problem is that you aren't attempting to implement Playable for your enum, you are trying to implement it for one of the enum's variants. Enum variants are not types.
As the error message tells you:
help: you can try using the variant's enum: `SceneType`
impl Playable for SceneType {
fn play() {}
}
See also:
Can struct-like enums be used as types?
Is there a way to use existing structs as enum variants?

If you want to implement a trait for Playable (i.e. for all enum variants) then the answer is quite simply: Yes you can. And Shepmaster's answer details how to do that.
However, if you really only want one enum variant to be Playable and not the others, then Rust doesn't directly support that, but there's an idiom I've seen used to emulate it. Instead of
enum MyEnum {
A(i32, i32),
B(String),
}
you explicitly implement each enum variant as a separate struct, so
enum MyEnum {
A(A),
B(B),
}
struct A {
x: i32,
y: i32,
}
struct B {
name: String,
}
And then you can impl Playable for A without impl Playable for B. Whenever you want to call it, pattern match the MyEnum and, if you get an A, you can call play in your example on the result of the pattern match.
I don't recommend using this pattern for every enum you write, as it does make the code a decent bit more verbose and requires some boilerplate constructor methods to make it palatable. But for complicated enums with a lot of options, this sort of pattern can make the code easier to reason about, especially if you have a lot of traits or functions that only really apply to a couple of the enum possibilities.

Edit: Truly apologize; this answer isn't about
every SceneType can be played statically
Old answer
Try generics:
#[derive(Debug, Copy, Clone)]
pub enum SceneType <Cutscene>
where
Cutscene: Playable
{
Cutscene(Cutscene),
Game,
Menu,
Pause,
Credits,
Exit,
}
//We want to guarantee every SceneType can be played statically
// Notice: add `pub` as enum
pub trait Playable {
fn play();
}
// create struct for inner of SceneType::Cutscene
struct Cutscene {
// ...
}
// impl to specific Cutscene
impl Playable for Cutscene {
fn play() {}
}
Test it:
fn main () {
let cutscene = Cutscene{};
let scenetype = SceneType::Cutscene(cutscene);
}
A downside I realized is that the generics are static. When there are more than one generics for an enum, all generics must be specified.
enum E <A, B>
where
A: SomeTrait1,
B: SomeTrait2,
{
Enum1(A),
Enum2(B),
}
trait SomeTrait1 {}
trait SomeTrait2 {}
struct S1 {}
impl SomeTrait1 for S1{}
struct S2 {}
impl SomeTrait2 for S2{}
struct X1 {}
impl SomeTrait1 for X1{}
fn main () {
// specify the generics
E::<S1, S2>::Enum1(S1{});
E::<X1, S2>::Enum1(X1{});
//error[E0282]: type annotations needed
// --> src/main.rs:26:5
// |
//33 | E::Enum1(S1{});
// | ^^^^^^^^ cannot infer type for type parameter `B` declared on the enum `E`
// E::Enum1(S1{});
// E::Enum1(X1{});
}

Related

generic callback with data

There is already a very popular question about this topic but I don;t fully understand the answer.
The goal is:
I need a list (read a Vec) of "function pointers" that modify data stored elsewhere in a program. The simplest example I can come up with are callbacks to be called when a key is pressed. So when any key is pressed, all functions passed to the object will be called in some order.
Reading the answer, it is not clear to me how I would be able to make such a list. It sounds like I would need to restrict the type of the callback to something known, else I don't know how you would be able to make an array of it.
It's also not clear to me how to store the data pointers/references.
Say I have
struct Processor<CB>
where
CB: FnMut(),
{
callback: CB,
}
Like the answer suggests, I can't make an array of processors, can I? since each Processor is technically a different type depending on the generic isntantiation.
Indeed, you can't make a vector of processors. Usually, closures all have different, innominable types. What you want instead are trait objects, which allow you to have dynamic dispatch of callback calls. Since those are not Sized, you'd probably want to put them in a Box. The final type is Vec<Box<dyn FnMut()>>.
fn add_callback(list: &mut Vec<Box<dyn FnMut()>>, cb: impl FnMut() + 'static) {
list.push(Box::new(cb))
}
fn run_callback(list: &mut [Box<dyn FnMut()>]) {
for cb in list {
cb()
}
}
see the playground
If you do like that, however, you might have some issues with the lifetimes (because your either force to move-in everything, or only modify values that life for 'static, which isn't very convenient. Instead, the following might be better
#[derive(Default)]
struct Producer<'a> {
list: Vec<Box<dyn FnMut() + 'a>>,
}
impl<'a> Producer<'a> {
fn add_callback(&mut self, cb: impl FnMut() + 'a) {
self.list.push(Box::new(cb))
}
fn run_callbacks(&mut self) {
for cb in &mut self.list {
cb()
}
}
}
fn callback_1() {
println!("Hello!");
}
fn main() {
let mut modified = 0;
let mut prod = Producer::default();
prod.add_callback(callback_1);
prod.add_callback(
|| {
modified += 1;
println!("World!");
}
);
prod.run_callbacks();
drop(prod);
println!("{}", modified);
}
see the playground
Just a few things to note:
You manually have to drop the producer, otherwise Rust will complain that it will be dropped at the end of the scope, but it contains (through the closure) an exclusive reference to modified, which is not ok since I try to read it.
Current, run_callbacks take a &mut self, because we only require for a FnMut. If you wanted it to be only a &self, you'd need to replace FnMut with Fn, which means the callbacks can still modify things outside of them, but not inside.
Yes, all closures are differents type, so if you want to have a vec of different closure you will need to make them trait objects. This can be archieve with Box<dyn Trait> (or any smart pointer). Box<dyn FnMut()> implements FnMut(), so you can have Processor<Box<dyn FnMut()>> and can make a vec of them, and call the callbacks on them: playground

How do I filter a vector of an enum of different structs using a common field?

I found you can create a vector of different types of structs using an enum. When filtering the vector on a common field, such as id, the compiler doesn't know the type while iterating:
use chrono::{DateTime, Utc}; // 0.4.19
use serde::{Serialize, Deserialize}; // 1.0.126
#[derive(Deserialize, Debug, Serialize, Clone)]
pub enum TransactionsEnum {
TransactionOrderA(TransactionOrderA),
TransactionOrderB(TransactionOrderB),
}
#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct TransactionOrderA {
pub id: i64,
pub accountID: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionOrderB {
pub id: i64,
pub time: DateTime<Utc>,
}
fn transactions_filter(
transactions_vector: Vec<TransactionsEnum>,
x: i64,
) -> Vec<TransactionsEnum> {
transactions_vector
.into_iter()
.filter(|e| e.id >= x)
.collect()
}
error[E0609]: no field `id` on type `&TransactionsEnum`
--> src/lib.rs:27:23
|
27 | .filter(|e| e.id >= x)
| ^^
Sharing a common value in all enum values and Is there a way to directly access a field value in an enum struct without pattern matching? indirectly answer my question, but the answer provided here helped me understand why a match statement is necessary.
Those are not a common field, they are completely unrelated fields. The fact that they share a name is, as far as the Rust compiler is concerned, an insignificant coincidence. You need to use pattern matching to get the field from either case
impl TransactionsEnum {
pub fn id(&self) -> i64 {
match self {
TransactionsEnum::TransactionOrderA(value) => value.id,
TransactionsEnum::TransactionOrderB(value) => value.id,
}
}
}
transactions_vector
.into_iter()
.filter(|e| e.id() >= x) // Note the parentheses since we're calling a function now
.collect()

Can I alias fully qualified syntax?

I have some code where I have many instances of fully qualified syntax; as an example:
mod hal {
pub trait Backend {
type Device;
}
}
mod back {
pub struct Backend {}
impl ::hal::Backend for Backend {
type Device = i32;
}
}
fn main() {
let d: back::Backend::Device = 0;
}
playground
In order to avoid errors like:
error[E0223]: ambiguous associated type
--> src/main.rs:16:12
|
16 | let d: back::Backend::Device = 0;
| ^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<back::Backend as Trait>::Device`
Is there a nice way in which I can alias SomeType as SomeTrait? Then, wherever this instance of fully qualified syntax is needed, I can write:
<S>::associated_fn(...)
Note that this error does not occur because there are actually multiple implementations of some trait's definition (which is what FQS is supposed to handle, according to The Rust Programming Language).
In your example, you can rename some of the parts so you can refer to them without conflicts in the shortened form:
use hal::Backend;
use back::Backend as BackendImpl;
fn main() {
let d: <BackendImpl as Backend>::Device = 0;
}
You may also consider defining a type alias, which is less ambiguous to access:
mod hal {
pub trait Backend {
type Device;
}
}
mod back {
pub struct Backend {}
pub type Device = i32;
impl ::hal::Backend for Backend {
type Device = Device;
}
}
fn main() {
let d: back::Device = 0;
}
No, there is no way to alias the fully-qualified syntax. Doing so doesn't make sense to me as the whole point of this syntax is to be completely unambiguous.
All of this assumes that you actually need fully-qualified syntax. As the name indicates, there's usually shorter ways of writing the code. These are all equivalent if no other traits defining to_string are in scope and the type itself doesn't implement a method of the same name:
<i32 as ToString>::to_string(&42);
ToString::to_string(&42);
i32::to_string(&42);
42.to_string();
See also:
How to call a method when a trait and struct use the same name?
How do I disambiguate traits in Rust?

How to extend the lifetimes of Strings within functions of traits that require returning &str?

Problem
I am trying to implement the std::error::Error trait on a enum. Some elements of the enum are Enum Variants, and I would like to generate a different error message containing the data from the variant. However with the implementation below the formatted String that Deref to &str don't live long enough.
The general solution is to return a String. However, this is not an option here as the returned type must be &str as specified by the Error trait.
Example: Playground link
It is important to note that the variants may not contain usize, and might instead be another enum, or struct etc.
use std::fmt;
use std::fmt::{Display, Formatter};
use std::error;
#[derive(Debug)]
enum EnumError {
A,
B(usize),
C(usize),
D,
}
impl error::Error for EnumError {
fn description(&self) -> &str {
use EnumError::*;
match *self {
A => "A happened",
B(value) => &*format!("B happened info: {:?}", value),
C(value) => &*format!("B happened info: {:?}", value),
D => "D happened",
}
}
}
impl Display for EnumError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use std::error::Error;
write!(f, "{}", self.description())
}
}
fn main() {}
The string you create needs to be owned by something. When you create a local string in the method, you have to transfer its ownership to the caller. But since you have to return &str, this is not an option.
The way around it would be to store the string in the struct itself. You can declare the enum value as B(usize, String), put the description there when you create it, and then return it with
B(_, ref descr) => descr
To be frank, description is not supposed to be a terribly detailed message, it just needs to give a general description of what kind of error this is, this is why it returns &str. I didn't see instances of writing any dynamic data into the description in the standard library, usually it's just a static string. Display implementation is a different matter though, in there you can be much more verbose.

Am I using inheritance, borrowed pointers, and explicit lifetime annotations correctly?

I'm learning Rust, coming from an almost exclusively garbage collected background. So I want to make sure I'm getting off on the right foot as I write my first program.
The tutorial on the Rust site said I should be dubious of using pointers that aren't &. With that in mind, here's what I ended up with in my little class hierarchy (names changed to protect innocent). The gist is, I have two different entities, let's say Derived1 and Derived2, which share some behavior and structure. I put the common data into a Foo struct and common behavior into a Fooish trait:
struct Foo<'a> {
name: &'a str,
an_array: &'a [AnEnumType],
/* etc. */
}
struct Derived1<'a> {
foo: &'a Foo<'a>,
other_stuff: &'a str,
}
struct Derived2<'a> {
foo: &'a Foo<'a>,
/* etc. */
}
trait Fooish<'a> {
fn new(foo: &'a Foo<'a>) -> Self;
/* etc. */
}
impl<'a> Fooish<'a> for Derived1<'a> {
fn new(foo: &'a Foo<'a>) -> Derived1<'a> {
Derived1 { foo: foo, other_stuff: "bar" }
}
/* etc. */
}
/* and so forth for Derived2 */
My questions:
Am I "doing inheritance in Rust" more-or-less idiomatically?
Is it correct to use & pointers as struct fields here? (such as for string data, and array fields whose sizes vary from instance to instance? What about for Foo in Derived?)
If the answer to #2 is 'yes', then I need explicit lifetime annotations, right?
Is it common to have so many lifetime annotations everywhere as in my example?
Thanks!
I'd say that this is not idiomatic at all, but sometimes there are tasks which require stepping away from idiomatic approaches, it is just not clear if this is really such a case.
I'd suggest you to refrain from using ideas from OO languages which operate in terms of classes and inheritance - they won't work correctly in Rust. Instead you should think of your data in terms of ownership: ask yourself a question, should the given struct own the data? In other words, does the data belong to the struct naturally or it can be used independently somewhere?
If you apply this reasoning to your structures:
struct Foo<'a> {
name: &'a str,
an_array: &'a [AnEnumType],
/* etc. */
}
struct Derived1<'a> {
foo: &'a Foo<'a>,
other_stuff: &'a str,
}
struct Derived2<'a> {
foo: &'a Foo<'a>,
/* etc. */
}
you would see that it doesn't really make sense to encode inheritance using references. If Derived1 has a reference to Foo, then it is implied that this Foo is created somewhere else, and Derived1 is only borrowing it for a while. While this may be something you really want, this is not how inheritance works: inherited structures/classes usually contain their "parent" contents inside them; in other words, they own their parent data, so this will be more appropriate structure:
struct Foo<'a> {
name: &'a str,
an_array: &'a [AnEnumType],
/* etc. */
}
struct Derived1<'a> {
foo: Foo<'a>
other_stuff: &'a str,
}
struct Derived2<'a> {
foo: Foo<'a>,
/* etc. */
}
Note that Derived* structures now include Foo into them.
As for strings and arrays (string slices and array slices in fact), then yes, if you want to hold them in structures you do have to use lifetime annotations. However, it does not happen that often, and, again, designing structures based on ownership usually helps to decide whether this should be a slice or a dynamically allocated String or Vec. There is a nice tutorial on strings, which explains, among everything else, when you need to use owned strings and when you need slices. Same reasoning applies to &[T]/Vec<T>. In short, if your struct owns the string/array, you have to use String/Vec. Otherwise, consider using slices.

Resources