如何通过回调管理生命周期
How to manage lifetime through callbacks
我正在尝试创建一个函数,在给定回调的情况下,它可以对字符串执行一些操作,如果需要进行一些修改,returns 一个新分配的副本。我正在使用 std::borow::Cow 来做这样的事情,但是当我尝试使用回调来更改对字符串的操作执行方式时,问题就来了。
这是我正在尝试做的一个简化示例:
use std::borrow::Cow;
pub enum Error {
Test,
}
fn do_lower_case<'a> (s: &'a str) -> Result<Cow<'a, str>, Error>
{
let s = s.to_lowercase();
Ok(s.into())
}
fn say_hello<'a>(
f: impl Fn(&'a str) -> Result<Cow<'a, str>, Error>,
) -> Result<Cow<'a, str>, Error>
{
let s = String::from("Hello");
// Problem: We can not call the callback from here.
// Nevertheless we can call do_lower_case
let s = f(&s)?; // Comment this and uncomment next line works
//let s = do_lower_case(&s)?;
let s = s.into_owned();
Ok(s.into())
}
fn main() {
let res = say_hello(do_lower_case); // Callback is provided here
match res {
Ok(s) => println!("Result: {}", s),
Err(_) => println!("Could not do anything!"),
}
}
Rust 可以做这样的事情吗?
这是一个 link 到铁锈游乐场:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8607d8ac4c34d02236e82e30bcf51e2e
编译器报错,因为s
的生命周期总是比'a
所能提供的生命周期短。
要解决这个问题,您可以使用 higher-ranked lifetimes。您可以指定一个仅适用于闭包的生命周期,而不是 say_hello
函数中的通用生命周期。这是通过在 impl
和 Fn
之间添加 for<'a>
来完成的。在那之后,say_hello
也不能 return Cow<'a, str>
,并且需要 return String
.
fn say_hello(f: impl for<'a> Fn(&'a str) -> Result<Cow<'a, str>, Error>) -> Result<String, Error> {
let s = "Hello";
let s = f(&s)?;
let s = s.into_owned();
Ok(s)
}
或者,以下内容可能更具可读性:
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: for<'a> Fn(&'a str) -> Result<Cow<'a, str>, Error>,
{
let s = "Hello";
let s = f(&s)?;
let s = s.into_owned();
Ok(s)
}
也将 let s = String::from("Hello");
更改为 let s = "Hello";
。
在这种情况下,您还可以依赖 lifetime elision 并可选择使用占位符生命周期 '_
。那么就变成了:
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: Fn(&str) -> Result<Cow<str>, Error>,
{
或
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: Fn(&'_ str) -> Result<Cow<'_, str>, Error>,
{
您也可以像原来那样使用 impl Fn...
内联任何一个。
我正在尝试创建一个函数,在给定回调的情况下,它可以对字符串执行一些操作,如果需要进行一些修改,returns 一个新分配的副本。我正在使用 std::borow::Cow 来做这样的事情,但是当我尝试使用回调来更改对字符串的操作执行方式时,问题就来了。
这是我正在尝试做的一个简化示例:
use std::borrow::Cow;
pub enum Error {
Test,
}
fn do_lower_case<'a> (s: &'a str) -> Result<Cow<'a, str>, Error>
{
let s = s.to_lowercase();
Ok(s.into())
}
fn say_hello<'a>(
f: impl Fn(&'a str) -> Result<Cow<'a, str>, Error>,
) -> Result<Cow<'a, str>, Error>
{
let s = String::from("Hello");
// Problem: We can not call the callback from here.
// Nevertheless we can call do_lower_case
let s = f(&s)?; // Comment this and uncomment next line works
//let s = do_lower_case(&s)?;
let s = s.into_owned();
Ok(s.into())
}
fn main() {
let res = say_hello(do_lower_case); // Callback is provided here
match res {
Ok(s) => println!("Result: {}", s),
Err(_) => println!("Could not do anything!"),
}
}
Rust 可以做这样的事情吗? 这是一个 link 到铁锈游乐场: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8607d8ac4c34d02236e82e30bcf51e2e
编译器报错,因为s
的生命周期总是比'a
所能提供的生命周期短。
要解决这个问题,您可以使用 higher-ranked lifetimes。您可以指定一个仅适用于闭包的生命周期,而不是 say_hello
函数中的通用生命周期。这是通过在 impl
和 Fn
之间添加 for<'a>
来完成的。在那之后,say_hello
也不能 return Cow<'a, str>
,并且需要 return String
.
fn say_hello(f: impl for<'a> Fn(&'a str) -> Result<Cow<'a, str>, Error>) -> Result<String, Error> {
let s = "Hello";
let s = f(&s)?;
let s = s.into_owned();
Ok(s)
}
或者,以下内容可能更具可读性:
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: for<'a> Fn(&'a str) -> Result<Cow<'a, str>, Error>,
{
let s = "Hello";
let s = f(&s)?;
let s = s.into_owned();
Ok(s)
}
也将 let s = String::from("Hello");
更改为 let s = "Hello";
。
在这种情况下,您还可以依赖 lifetime elision 并可选择使用占位符生命周期 '_
。那么就变成了:
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: Fn(&str) -> Result<Cow<str>, Error>,
{
或
fn say_hello<F>(f: F) -> Result<String, Error>
where
F: Fn(&'_ str) -> Result<Cow<'_, str>, Error>,
{
您也可以像原来那样使用 impl Fn...
内联任何一个。