Skip to content

An observation/hack re the scope requirements for enum_dispatch

I've contrived an example project to demonstrate some surprising behavior I encountered using enum_dispatch and a related hack (that I don't necessarily recommend, but which is nonetheless interesting). I'm not sure this should be considered a bug, but, whatever it is, it seemed worth sharing, so here we are.

So as not to bury the lede too deep, let me say at the top that it seems that code fails to compile when the enum_display macro is used unless the enum and all of the types used by its variant constructors are in scope. However, there seems to be a workaround.

Initially, in my real project, I'd defined an enum and several traits that it implements in the same file. As I further developed the app, that file began to feel cluttered, so I started moving the traits out into another module. Without giving it too much thought, I moved them into a sibling module rather than a child module. Perhaps the reorganization was less than idiomatic—after all, what is an Animal that can't Eat, or Speaking without an Animal to do it? The example project represents that refactored state:

src/
├─ animal/          <-- contains the structs (these are all private) used by the enum's variants
├─ io/              <-- contains the traits
├─ animal.rs        <-- contains the Animal enum
├─ io.rs

The refactor went smoothly until I moved the last trait out into its own file, at which point I started getting compiler errors like these:

$ cargo run
   Compiling enum_dispatch_oddity v0.1.0 (/home/universalhandle/Code/enum_dispatch_oddity)
error[E0412]: cannot find type `Cat` in this scope
 --> src/io/eat.rs:4:1
  |
4 | #[enum_dispatch(Animal)]
  | ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
  |
note: struct `crate::animal::cat::Cat` exists but is inaccessible
 --> src/animal/cat.rs:3:1
  |
3 | pub struct Cat {}
  | ^^^^^^^^^^^^^^ not accessible
  = note: this error originates in the attribute macro `enum_dispatch` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `Dog` in this scope
 --> src/io/eat.rs:4:1
  |
4 | #[enum_dispatch(Animal)]
  | ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
  |
note: struct `crate::animal::dog::Dog` exists but is inaccessible
 --> src/animal/dog.rs:3:1
  |
3 | pub struct Dog {}
  | ^^^^^^^^^^^^^^ not accessible
  = note: this error originates in the attribute macro `enum_dispatch` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0412`.
error: could not compile `enum_dispatch_oddity` (bin "enum_dispatch_oddity") due to 2 previous errors

(The errors complain that Cat and Dog are not accessible to the eat module. Keeping Cat and Dog private was an intentional choice; I wanted only Animal exposed to the rest of the application.)

Adding a bogus trait with all the relevant items in scope is sufficient to make the compiler happy. Evidently, the enum_display macro needs the Animal enum and all of the types used by its variant constructors in scope; otherwise the code won't compile. However, interestingly:

  • the types for the variant constructors only need to be in scope for one usage of enum_dispatch; elsewhere it is sufficient for only the enum (Animal) to be in scope
  • this empty trait does not actually have to be implemented by the types in question (i.e., Cat and Dog)