How can I reduce code duplication in this function?

I have this function

fn render_composition(
    function_name: &str,
    base: &str,
    arg: Option<String>,
    dialect: &impl Dialect,
) -> String {
    match function_name {
        PLUS => match arg {
            None => base.to_owned(),
            Some(a) => format!("{} + {}", base, a),
        },
        MINUS => match arg {
            None => base.to_owned(),
            Some(a) => format!("{} - {}", base, a),
        },
        TIMES => match arg {
            None => base.to_owned(),
            Some(a) => format!("{} * {}", base, a),
        },
        DIVIDE => match arg {
            None => base.to_owned(),
            Some(a) => format!("{} / {}", base, a),
        },
        AGO => format!("{} - {}", Value::Now.render(dialect), base),
        FROM_NOW => format!("{} + {}", Value::Now.render(dialect), base),
        _ => base.to_owned(),
    }
}

I'm going to be adding many more arms to that top-level match, and I want to get rid of some of the code duplication here.

I tried starting my function body with the following code:

let required = |f: impl Fn(String) -> String| match arg {
    None => base.to_owned(),
    Some(a) => f(a),
};
match function_name {
    PLUS => required(|a| format!("{} + {}", base, a)),
    // ...
}

But I can't use impl Trait within a closure definition.

How can I define my render_composition function to be more concise? I'd like to eliminate the nested match statements and end up with one line per match arm.

You can match on a tuple of function_name and arg.

From another point of view. You're doing stringly typed operations. There's a probably a better way. (and your matches are all actually wildcards)

2 Likes

I agree with @erelde, but anyway, a couple observations:

  • Your closure attempt is basically => arg.map(...).unwrap_or_else(|| ...)
    let res = match function_name {
       SomeEnum::Plus => arg.map(|a| format!(...)),
       // ...
       SomeEnum::Ago => Some(format!(...)),
       _ => None, // probably goes away
    };
    
    res.unwrap_or_else(|| base.to_owned())
    
  • This would handle most of what's in the example anyway
    let required = |op: &str| -> String {
        arg.map(|a| format!("{base} {op} {a}"))
           .unwrap_or_else(|| base.to_owned())
    };
    // => required("+"),
    
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.