简体   繁体   中英

Rust won't let me return an instance of a type from a match, keeps thinking I'm trying to return a value

According to this answer to this questions I need to do the following to return an instance of a Trait :

trait Shader {}

struct MyShader;
impl Shader for MyShader {}

struct GraphicsContext;

impl GraphicsContext {
    fn create_shader(&self) -> impl Shader {
        let shader = MyShader;
        shader
    }
}

but when I try to do it:

pub trait Component { }

struct Kind {}

struct Location {}

impl Component for Kind {}

impl Component for Location {}

pub fn get(comp_name: &String) -> impl Component {
    match comp_name.as_ref() {
        "kind"      => Kind,
        "location"  => Location
    }
}

I just get errors:

error[E0423]: expected value, found struct Kind --> src/main.rs:17:24

  | 17 | "kind" => Kind, | ^^^^ did you mean `Kind { /* fields */ }`? 

error[E0423]: expected value, found struct Location --> src/main.rs:18:24

  | 18 | "location" => Location | ^^^^^^^^ did you mean `Location { /* fields */ >}`? 

That impl Component as the return type is basically a T where T: Component , where the T is chosen by the function itself rather than by the caller.

T can be Kind , T can be Location , but T can't be both at once.

Two solutions:

  1. Dynamically: Return a Box<dyn Component> and return Box::new(Kind{}) or Box::new(Location{}) . The disadvantage is that it causes an heap allocation.

  2. Statically, by returning an enum :

enum KindOrLocation {
    Kind(Kind),
    Location(Location),
}

To make this usable as a Component , you can implement Deref<Target = dyn Component> :

impl Deref for KindOrLocation {
    type Target = dyn Component + 'static;
    fn deref(&self) -> &Self::Target {
        match self {
            KindOrLocation::Kind(x) => x,
            KindOrLocation::Location(x) => x,
        }
    }
}

The disadvantage here is that you have to write this boilerplate code.


By the way:

  • If you define a struct with {} like struct Kind {} , you create an object of it by writing Kind{} , not just Kind .
  • You need to handle the _ case in your match: _ => panic!() or something.
  • Don't take a &String , but take a &str instead. Then it works for both &String and &str .

The compiler has to know how much room to reserve on the stack at compile time. As the linked answer mentions, if the concrete return type is conditional, the amount of space that would be needed cannot be known until runtime. That's what this bit of the answer is referring to:

It does have limitations, such as [...] it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.

You should use the second form of the function shown in that answer if you want to conditionally return either a Kind or a Location . In that case, the Kind or the Location will be created on the heap instead of the stack; the stack will contain a Box that owns that heap reference, and Box is a type whose size is known at compile time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM