ruser9575ba6f
ruser9575ba6f

Reputation: 298

How do I perform a replacement using a formatted string from a regex capture group?

I am doing multiple replacements at once using the regex crate:

extern crate regex;

use regex::{Captures, Regex};

fn transform(string: &str) {
    let rgx = Regex::new(r"(\n)|(/\w+)").unwrap();
    let res = rgx.replace_all(string, |caps: &Captures| {
        if caps.get(1).is_some() {
            return " ";
        }
        match caps.get(2).map(|m: regex::Match| m.as_str()) {
            Some(z) => return "nope", // how to return formatted z instead?
            None => (),
        }
        unreachable!();
    });
    println!("{}", res);
}

fn main() {
    transform("no errors");
    transform("big\nbad\n/string");
}

Output as expected:

no errors
big bad nope

Instead of "nope", I would like to return z formatted in some way instead. format! doesn't seem like it can be used here due to String / lifetime issues:

match caps.get(2).map(|m: regex::Match| m.as_str()) {
    Some(z) => return format!("cmd: {}", z),
    None => (),
}
error[E0308]: mismatched types
  --> src/main.rs:12:31
   |
12 |             Some(z) => return format!("cmd: {}", z),
   |                               ^^^^^^^^^^^^^^^^^^^^^ expected &str, found struct `std::string::String`
   |
   = note: expected type `&str`
              found type `std::string::String`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

What should be done instead?

Upvotes: 0

Views: 758

Answers (1)

Shepmaster
Shepmaster

Reputation: 432109

Note in the error message:

expected &str

It expects a &str because that's the first type returned by your closure:

return " ";

A closure / function can have only one return type, not two.

The simplest fix is to return a String in both cases:

let res = rgx.replace_all(string, |caps: &Captures| {
    if caps.get(1).is_some() {
        return String::from(" ");
    }
    let m = caps.get(2).unwrap();
    format!("cmd: {}", m.as_str())
});

To be slightly more efficient, you can avoid the String allocation for the space character:

use std::borrow::Cow;
let res = rgx.replace_all(string, |caps: &Captures| {
    if caps.get(1).is_some() {
        return Cow::from(" ");
    }
    let m = caps.get(2).unwrap();
    Cow::from(format!("cmd: {}", m.as_str()))
});

playground

I've also replaced the match with the => () arm paired with the unreachable! with the shorter unwrap.

See also:

Upvotes: 2

Related Questions