如何组合对 Regex::replace_all 的两次调用?
How to compose two calls to Regex::replace_all?
Regex::replace_all
具有签名 fn (text: &str) -> Cow<str>
。如何写两个调用,f(g(x))
,给出相同的签名?
这是我正在尝试编写的一些代码。这将两个调用分成两个函数,但我也无法让它在一个函数中工作。这是我的 lib.rs
在一个新的 Cargo 项目中:
#![allow(dead_code)]
/// Plaintext and HTML manipulation.
use lazy_static::lazy_static;
use regex::Regex;
use std::borrow::Cow;
lazy_static! {
static ref DOUBLE_QUOTED_TEXT: Regex = Regex::new(r#""(?P<content>[^"]+)""#).unwrap();
static ref SINGLE_QUOTE: Regex = Regex::new(r"'").unwrap();
}
fn add_typography(text: &str) -> Cow<str> {
add_double_quotes(&add_single_quotes(text)) // Error! "returns a value referencing data owned by the current function"
}
fn add_double_quotes(text: &str) -> Cow<str> {
DOUBLE_QUOTED_TEXT.replace_all(text, "“$content”")
}
fn add_single_quotes(text: &str) -> Cow<str> {
SINGLE_QUOTE.replace_all(text, "’")
}
#[cfg(test)]
mod tests {
use crate::{add_typography};
#[test]
fn converts_to_double_quotes() {
assert_eq!(add_typography(r#""Hello""#), "“Hello”");
}
#[test]
fn converts_a_single_quote() {
assert_eq!(add_typography("Today's Menu"), "Today’s Menu");
}
}
这是我能想到的最好的方法,但是当链接三个或四个函数时,这会很快变得丑陋:
fn add_typography(input: &str) -> Cow<str> {
match add_single_quotes(input) {
Cow::Owned(output) => add_double_quotes(&output).into_owned().into(),
_ => add_double_quotes(input),
}
}
A Cow
包含 maybe-owned 数据。
我们可以从 replace_all
函数的作用推断它 return 只有在替换没有发生的情况下才借用数据,否则它必须 return 新的、拥有的数据。
当内部调用进行替换而外部调用没有进行替换时,就会出现问题。在这种情况下,外部调用将简单地将其输入作为 Cow::Borrowed
传递,但它借用内部调用 return 的 Cow::Owned
值,其数据现在属于 Cow
add_typography()
本地的临时文件。因此,该函数将 return 一个 Cow::Borrowed
,但会从临时借用,这显然不是 memory-safe。
基本上,此函数只会在任一调用均未进行替换时 return 借用数据。我们需要的是一个助手,它可以在 returned Cow
本身拥有时通过调用层传播 owned-ness。
我们可以在 Cow
之上构造一个 .map()
扩展方法,它正是这样做的:
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()),
}
}
}
现在您的呼叫站点可以保持干净整洁:
fn add_typography(text: &str) -> Cow<str> {
add_single_quotes(text).map(add_double_quotes)
}
Regex::replace_all
具有签名 fn (text: &str) -> Cow<str>
。如何写两个调用,f(g(x))
,给出相同的签名?
这是我正在尝试编写的一些代码。这将两个调用分成两个函数,但我也无法让它在一个函数中工作。这是我的 lib.rs
在一个新的 Cargo 项目中:
#![allow(dead_code)]
/// Plaintext and HTML manipulation.
use lazy_static::lazy_static;
use regex::Regex;
use std::borrow::Cow;
lazy_static! {
static ref DOUBLE_QUOTED_TEXT: Regex = Regex::new(r#""(?P<content>[^"]+)""#).unwrap();
static ref SINGLE_QUOTE: Regex = Regex::new(r"'").unwrap();
}
fn add_typography(text: &str) -> Cow<str> {
add_double_quotes(&add_single_quotes(text)) // Error! "returns a value referencing data owned by the current function"
}
fn add_double_quotes(text: &str) -> Cow<str> {
DOUBLE_QUOTED_TEXT.replace_all(text, "“$content”")
}
fn add_single_quotes(text: &str) -> Cow<str> {
SINGLE_QUOTE.replace_all(text, "’")
}
#[cfg(test)]
mod tests {
use crate::{add_typography};
#[test]
fn converts_to_double_quotes() {
assert_eq!(add_typography(r#""Hello""#), "“Hello”");
}
#[test]
fn converts_a_single_quote() {
assert_eq!(add_typography("Today's Menu"), "Today’s Menu");
}
}
这是我能想到的最好的方法,但是当链接三个或四个函数时,这会很快变得丑陋:
fn add_typography(input: &str) -> Cow<str> {
match add_single_quotes(input) {
Cow::Owned(output) => add_double_quotes(&output).into_owned().into(),
_ => add_double_quotes(input),
}
}
A Cow
包含 maybe-owned 数据。
我们可以从 replace_all
函数的作用推断它 return 只有在替换没有发生的情况下才借用数据,否则它必须 return 新的、拥有的数据。
当内部调用进行替换而外部调用没有进行替换时,就会出现问题。在这种情况下,外部调用将简单地将其输入作为 Cow::Borrowed
传递,但它借用内部调用 return 的 Cow::Owned
值,其数据现在属于 Cow
add_typography()
本地的临时文件。因此,该函数将 return 一个 Cow::Borrowed
,但会从临时借用,这显然不是 memory-safe。
基本上,此函数只会在任一调用均未进行替换时 return 借用数据。我们需要的是一个助手,它可以在 returned Cow
本身拥有时通过调用层传播 owned-ness。
我们可以在 Cow
之上构造一个 .map()
扩展方法,它正是这样做的:
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()),
}
}
}
现在您的呼叫站点可以保持干净整洁:
fn add_typography(text: &str) -> Cow<str> {
add_single_quotes(text).map(add_double_quotes)
}