Troy Daniels
Troy Daniels

Reputation: 3598

Store a value locally as a dynamic trait type in Rust

I have code that is somewhat like

trait Checker {
   fn check(&self, value: &str) -> bool;
}

struct ShortChecker {}
impl Checker for ShortChecker {
   fn check(&self, value: &str) -> bool {
      value.len() < 5
   }
}

struct PrefixChecker {
   prefix: String,
}
impl Checker for PrefixChecker {
   fn check(&self, value: &str) -> bool {
      value.starts_with(self.prefix)
   }
}

fn check(value: &str, which: &str) -> bool {
   let checker = if which == "short" {
      ShortChecker { }
   } else if which == "prefix" {
      PrefixChecker { prefix: "xyzzy".to_string() }
   } else { 
      panic!("Unknown option");
   };

   checker.check(value)
}

This fails to compile, since the different arms of the if statement return different types (ShortChecker vs. PrefixChecker). All I need is that there is local variable of type Checker. My first attempt at fixing it was

let checker: Checker = ...

which gave a warning that traits without dyn are deprecated. So I tried

let checker: dyn Checker = ...

That fails with E0308 because

    = note: expected trait object `dyn Checker`
                     found struct `PrefixChecker`

If I was trying to store this in a struct to use later, I know I would need a Box<dyn Checker> (or Rc or ...). Do I also need something like that for a local variable? Since the variable is local, the compiler can reason about when it will go out of scope, so it seems that wrapping it could be unnecessary.

Is there a way to assign an object to a trait-typed local variable without wrapping it?

Upvotes: 1

Views: 774

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70850

You can use Box, but there's another neat trick you can use:

fn check(value: &str, which: &str) -> bool {
    let (short_checker, prefix_checker);
    let checker: &dyn Checker = if which == "short" {
        short_checker = ShortChecker {};
        &short_checker
    } else if which == "prefix" {
        prefix_checker = PrefixChecker {
            prefix: "xyzzy".to_string(),
        };
        &prefix_checker
    } else {
        panic!("Unknown option");
    };

    checker.check(value)
}

Upvotes: 9

Related Questions