我正在使用一个特征和一个返回盒装迭代器的方法。由于迭代器将使用 self
和 foo
的参数,所有这些都被限制在相同的生命周期内:
pub trait Foo {
fn foo<'a>(&'a self, txt: &'a str) -> Box<Iterator<Item = String> + 'a>;
}
我想围绕这个方法构建一个函数:
fn foo_int<'a, F: Foo>(f: &'a F, val: i32) -> impl Iterator<Item = String> + 'a {
let txt = format!("{}", val);
f.foo(&txt)
}
但这并不能编译,因为:
error[E0515]: cannot return value referencing local variable `txt`
--> src/lib.rs:7:5
|
7 | f.foo(&txt)
| ^^^^^^----^
| | |
| | `txt` is borrowed here
| returns a value referencing data owned by the current function
我理解为什么会发生这种情况,这也是有道理的,但似乎应该有办法绕过它。毕竟,这就是闭包(使用 move
关键字)所做的:它们获得了需要“带走它们”的值的所有权。
是否有使用闭包或其他方式重写 foo_int
函数的巧妙方法?
实际上,在处理我的最小示例时,我想出了一个(某种程度上)解决问题的想法。可用in the playground .
想法是将 foo
的参数和返回的迭代器包装在一个专用类型中,它本身通过委托(delegate)给内部迭代器来实现 Iterator
特性。
pub struct Bar<'a>(String, Box<Iterator<Item = String> + 'a>);
impl<'a> Bar<'a> {
fn new<F: Foo>(foo: &'a F, txt: String) -> Bar<'a> {
let itr = foo.foo(unsafe { &*(&txt[..] as *const str) });
Bar(txt, itr)
}
}
impl<'a> Iterator for Bar<'a> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
self.1.next()
}
}
fn foo_int<'a, F: Foo>(f: &'a F, val: i32) -> impl Iterator<Item = String> + 'a {
Bar::new(f, format!("{}", val))
}
不过,由于以下原因,我对该解决方案并不完全满意。
对于这个特定的方法来说是相当具体的;对于具有其他参数的另一种方法,我需要不同的包装器类型。
它使用了不安全的代码。我相信这个没问题(因为 String
的内部 &str
通过移动是稳定的),但它可能更难(如果不是不可能的话)用其他类型实现。
它迫使我重新实现Iterator
。虽然这个最小版本有效,但它不是最优的,因为底层迭代器可能已经优化了某些方法的实现,我在这里通过依赖默认实现来“松散”。手动实现 Iterator
的所有方法(将它们传递给 self.1
)既乏味又脆弱(如果以后的版本添加新方法,我将不得不添加它们以及)。
所以仍然欢迎任何更好的解决方案...
编辑:@Shepmaster 的论点说服了我;这实际上是存储值和对它的引用的老问题。所以它是不安全的,我的解决方案不是通用的,因为一般来说,它可能行不通:-/