PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Tuesday, November 15, 2022

[FIXED] How do I bubble up an error from the closure passed to regex::Regex::replace?

 November 15, 2022     error-handling, regex, rust     No comments   

Issue

I have a function which performs string replacement in-place via regex::Regex::replace via a closure which performs some operations on the Captures:

pub fn solve_dice_expression(expression: String) -> Result<i64, Box<dyn Error>> {
    lazy_static! {
        static ref PATTERN: Regex = Regex::new(r"(\d+)d(\d+)").expect("Problem compiling regex");
    }

    // For every match on the Dice expression regex, roll it in-place.
    let rolled_expression = PATTERN.replace(&expression, |caps: &Captures| {
        let diceroll_str = &caps.get(0).unwrap().as_str().to_string();
        let dice = Dice::from_string(&diceroll_str).unwrap();
        return format!("{}", roll_dice(&mut rng, &dice));
    });

    // Calculate the result
    let result = eval(&rolled_expression)?.as_int()?;

    return Ok(result);
}

I try to have errors bubble up by returning Result<..., Box<dyn Error>>, which mostly works via ?. However, in the closure passed to regex::Regex::replace, I am not sure how to propagate any possible errors that could happen since it expects a closure returning a String and not a Result.

What would be the correct way to handle errors happening in that closure?


Solution

You cannot easily.

What you can do is smuggle out the error in a mutable Option, then check that after the replacement is complete:

use regex::{Captures, Regex}; // 1.3.3
use std::borrow::Cow;

type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;

fn example<'s>(r: &Regex, s: &'s str) -> Result<Cow<'s, str>> {
    let mut error = None;

    let v = r.replace(s, |caps: &Captures| {
        // TODO: Optimize by checking if we had an error already and exit early

        let digits = caps.get(0).unwrap().as_str();

        // Uncomment to see the failure
        // let digits = "bad";

        match digits.parse::<i32>() {
            Ok(v) => Cow::Owned((v + 1).to_string()),
            Err(e) => {
                // Save the error somewhere
                error = Some(e);
                // We are going to discard the replacement,
                // so it doesn't matter what we return
                Cow::Borrowed("")
            }
        }
    });

    match error {
        Some(e) => Err(Box::new(e)),
        None => Ok(v),
    }
}

fn main() {
    let r = Regex::new(r"\d+").unwrap();
    let v = example(&r, "1");
    println!("{:?}", v);
}

You could also potentially implement the Replacer trait on your own type to streamline this and maybe slightly optimize it more.



Answered By - Shepmaster
Answer Checked By - Clifford M. (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing