如何编写对 Regex::replace_all 的两个调用?

Dog*_*her 3 regex rust

Regex::replace_all有签名fn (text: &str) -> Cow<str>。对此的两次调用如何编写,f(g(x))给出相同的签名?

\n

这是我正在尝试编写的一些代码。这将两个调用分成两个函数,但我也无法让它在一个函数中工作。这是我lib.rs在一个新的 Cargo 项目中的情况:

\n
#![allow(dead_code)]\n\n/// Plaintext and HTML manipulation.\n\nuse lazy_static::lazy_static;\nuse regex::Regex;\nuse std::borrow::Cow;\n\nlazy_static! {\n    static ref DOUBLE_QUOTED_TEXT: Regex = Regex::new(r#""(?P<content>[^"]+)""#).unwrap();\n    static ref SINGLE_QUOTE:       Regex = Regex::new(r"\'").unwrap();\n}\n\n\nfn add_typography(text: &str) -> Cow<str> {\n    add_double_quotes(&add_single_quotes(text)) // Error! "returns a value referencing data owned by the current function"\n}\n\nfn add_double_quotes(text: &str) -> Cow<str> {\n    DOUBLE_QUOTED_TEXT.replace_all(text, "\xe2\x80\x9c$content\xe2\x80\x9d")\n}\n\nfn add_single_quotes(text: &str) -> Cow<str> {\n    SINGLE_QUOTE.replace_all(text, "\xe2\x80\x99")\n}\n\n\n#[cfg(test)]\nmod tests {\n    use crate::{add_typography};\n\n    #[test]\n    fn converts_to_double_quotes() {\n        assert_eq!(add_typography(r#""Hello""#), "\xe2\x80\x9cHello\xe2\x80\x9d");\n    }\n\n    #[test]\n    fn converts_a_single_quote() {\n        assert_eq!(add_typography("Today\'s Menu"), "Today\xe2\x80\x99s Menu");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是我能想到的最好的方法,但是当组合三个或四个函数时,这会很快变得丑陋:

\n
fn add_typography(input: &str) -> Cow<str> {\n    match add_single_quotes(input) {\n        Cow::Owned(output) => add_double_quotes(&output).into_owned().into(),\n        _                  => add_double_quotes(input),\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

cdh*_*wie 9

ACow包含可能拥有的数据。

我们可以从该函数的replace_all作用推断出,仅当替换未发生时它才返回借用的数据,否则它必须返回新的拥有的数据。

当内部调用进行替换但外部调用没有进行替换时,就会出现问题。在这种情况下,外部调用将简单地通过 as 传递其输入Cow::Borrowed,但它借用Cow::Owned内部调用返回的值,该值现在属于Cow本地的临时数据add_typography()。因此,该函数将返回 a Cow::Borrowed,但会从临时借用,这显然不是内存安全的。

基本上,当任一调用都没有进行替换时,此函数只会返回借用的数据。Cow我们需要的是一个助手,只要返回值本身拥有所有权,它就可以通过调用层传播所有权。

.map()我们可以在此基础上构造一个扩展方法Cow,其作用正是如此:

use std::borrow::{Borrow, Cow};

trait CowMapExt<'a, B>
    where B: 'a + ToOwned + ?Sized
{
    fn map<F>(self, f: F) -> Self
        where F: for <'b> FnOnce(&'b B) -> Cow<'b, B>;
}

impl<'a, B> CowMapExt<'a, B> for Cow<'a, B>
    where B: 'a + ToOwned + ?Sized
{
    fn map<F>(self, f: F) -> Self
        where F: for <'b> FnOnce(&'b B) -> Cow<'b, B>
    {
        match self {
            Cow::Borrowed(v) => f(v),
            Cow::Owned(v) => Cow::Owned(f(v.borrow()).into_owned()),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您的调用站点可以保持干净整洁:

fn add_typography(text: &str) -> Cow<str> {
    add_single_quotes(text).map(add_double_quotes)
}
Run Code Online (Sandbox Code Playgroud)