我对铁锈还很陌生,我正在与终身的东西rn作斗争。所以基本上我在构造一个结构,我想在这个结构中存储一个闭包。但其中一个被捕获的变量的寿命不够长。
struct HtmlOut<'a> {
echo_escaped: &'a dyn Fn(&String)
}
// not really implemented yet
fn escape(s: &String) -> &String {
return s;
}
impl<'a> HtmlOut<'a> {
fn new(echo: &'a dyn Fn(&String)) -> HtmlOut<'a> {
let echo_element_escaped_cls = |s: &String| { echo(escape(s)) };
let echo_element_escaped: &'a dyn Fn(&String) = &echo_element_escaped_cls;
return HtmlOut {
echo_escaped: echo_element_escaped
}
}
}
我有两个错误:
|s: &String| { echo(escape(s)) };
------------ ^^^^ borrowed value does not live long enough
和
&'a dyn Fn(&String) = &echo_element_escaped_cls;
------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
|
type annotation requires that `echo_element_escaped_cls` is borrowed for `'a`
我的问题是:
- 当所有内容都用相同的生存期说明符
'a
进行注释时,为什么echo的生存期不够长 - 如何重写此代码,使
echo
的寿命足够长
您的代码不能像编写的那样工作,因为引用必须引用某些东西。Rust没有GC,所以不能只在函数中创建一个值,创建对它的引用,然后从函数返回引用。您需要拥有值,这可以通过将值存储在结构中来实现,或者在dyn Trait
特征对象的情况下,通过装箱来实现:
struct HtmlOut {
echo_escaped: Box<dyn Fn(&str)>,
}
(我还修改了签名以接受&str
而不是&String
,后者更灵活,工作效果也很好,详细信息请参阅此处。(
另一个问题是escape
函数——要转义字符串,它必须能够创建转义字符串,而不仅仅是返回对其他人创建的字符串的引用。它需要接受&mut String
来修改字符串,或者返回一个拥有的String
:
// not really implemented yet
fn escape(s: &str) -> String {
s.to_string()
}
最后,构造函数需要接受echo
作为一个拥有的值,这样它就可以移动到闭包中:
impl HtmlOut {
fn new(echo: Box<dyn Fn(&str)>) -> HtmlOut {
let echo_element_escaped_cls = move |s: &str| echo(&escape(s));
let echo_element_escaped = Box::new(echo_element_escaped_cls);
return HtmlOut {
echo_escaped: echo_element_escaped,
};
}
}
为了获得最大的灵活性,构造函数甚至可以接受任意闭包,并将装箱作为自己的实现细节:
fn new<F: Fn(&str) + 'static>(echo: F) -> HtmlOut {
let echo = Box::new(echo);
let echo_element_escaped_cls = move |s: &str| echo(&escape(s));
let echo_element_escaped = Box::new(echo_element_escaped_cls);
return HtmlOut {
echo_escaped: echo_element_escaped,
};
}
游乐场