[英]Specifying lifetimes when using the factory pattern in Rust
The following code doesn't compile: 以下代码无法编译:
trait Phone {
fn call(&self);
}
struct IPhone<'a> {
my_str: &'a str
}
impl<'a> Phone for IPhone<'a> {
fn call(&self) {
print!("{}", self.my_str);
}
}
trait Factory<'a, P: Phone> {
fn new_phone(&self, ms: &'a str) -> P;
}
struct IPhoneFactory;
impl<'a> Factory<'a, IPhone<'a>> for IPhoneFactory {
fn new_phone(&self, ms: &'a str) -> IPhone<'a> {
return IPhone {
my_str: ms
};
}
}
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) {
for _ in 0..10 {
let s = String::new();
let p = f.new_phone(s.as_str());
p.call();
}
}
fn main() {
call_phone(IPhoneFactory);
}
I get the following error: 我收到以下错误:
error: `s` does not live long enough
let p = f.new_phone(s.as_str());
^
note: reference must be valid for the lifetime 'a as defined on the block at 28:53...
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) {
^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 30:30
let s = String::new();
^
I want to be able to have a factory that returns an abstract class, but when that class takes a reference I can't figure out how to specify the lifetime properly. 我希望能够有一个返回抽象类的工厂,但是当该类接受引用时,我无法弄清楚如何正确地指定生命周期。
You're right about that: 你是对的:
There is no reason for the reference to live as long as the factory, it only needs to live as long as the object the factory is creating (the factory itself doesn't store a reference to the string).
没有理由只要工厂存在引用,它只需要工厂生成的对象就可以存活(工厂本身不存储对字符串的引用)。
But the bound on call_phone
says something different 但是
call_phone
上的call_phone
说明了一些不同的东西
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { ... }
That code says that there's a single lifetime for the whole factory, which will be used for each phone . 该代码表示整个工厂只有一个生命周期 ,将用于每部手机 。 You want something different, you want to say that
f
is a good factory for any lifetime : 你想要不同的东西,你想说
f
对于任何一生来说都是一个好工厂:
fn call_phone<..., F: for<'a> Factory<'a, ...>>(f: F) { ... }
The other problem is that in the Factory
trait definition: 另一个问题是在
Factory
特征定义中:
trait Factory<'a, P: Phone> {
fn new_phone(&self, ms: &'a str) -> P;
}
There's nothing tying lifetime of P
to ms
. P
到ms
生命周期没有任何关系。 The trait definition allows the returned phone to outlive the string, which should definitely be forbidden for the IPhone
implementation! 特征定义允许返回的电话比字符串更长,这绝对不应该被
IPhone
实现! So, to fix it, we add a lifetime parameter to the Phone
trait: 因此,为了解决这个问题,我们在
Phone
特性中添加了一个生命周期参数:
trait Phone<'a> {
fn call(&self);
}
But there's still one problem. 但仍有一个问题。 We can't really write that signature:
我们不能写那个签名:
fn call_phone<P: ???, F: for<'a> Factory<'a, P<'a>>(f: F) { ... }
Since we want P
to be not a type, but rather a family of types (more precisely, a lifetime → type
constructor). 因为我们希望
P
不是一个类型,而是一个类型族(更准确地说,是一个lifetime → type
构造函数)。 Remember, the phone in each iteration of loop has a different type (since the lifetime is a part of a type, and lifetimes in different iterations of loops are different). 请记住,循环的每次迭代中的电话具有不同的类型(因为生命周期是类型的一部分,并且循环的不同迭代中的生命期是不同的)。
Ability to express such a signature is planned for the future Rust, but for now, we have to make a workaround and make the phone associated type of Factory
trait: 表示这样的签名的能力计划用于未来的Rust,但是现在,我们必须制定解决方法并使手机相关类型的
Factory
特性:
trait Phone<'a> {
fn call(&self);
}
struct IPhone<'a> {
my_str: &'a str
}
impl<'a> Phone<'a> for IPhone<'a> {
fn call(&self) {
println!("{}", self.my_str);
}
}
trait Factory<'a> {
type Output: Phone<'a>;
fn new_phone(&self, ms: &'a str) -> Self::Output;
}
struct IPhoneFactory;
impl<'a> Factory<'a> for IPhoneFactory {
type Output = IPhone<'a>;
fn new_phone(&self, ms: &'a str) -> IPhone<'a> {
IPhone {
my_str: ms
}
}
}
fn call_phone<F: for<'a> Factory<'a>>(f: F) {
for i in 0..10 {
let s = i.to_string();
let p = f.new_phone(&s);
p.call();
}
}
fn main() {
call_phone(IPhoneFactory);
}
Associated type allows the factory to produce only one kind of product, which is maybe what you wanted. 相关类型允许工厂只生产一种产品,这可能是你想要的。 If you want different implementations of
Factory
to have different Output
s, you can achieve this by using phantom types: 如果您希望
Factory
不同实现具有不同的Output
,则可以使用幻像类型来实现:
trait Phone<'a> {
type Phantom;
fn call(&self);
}
enum IPhonePhantom {}
struct IPhone<'a> {
my_str: &'a str
}
impl<'a> Phone<'a> for IPhone<'a> {
type Phantom = IPhonePhantom;
fn call(&self) {
println!("{}", self.my_str);
}
}
trait Factory<'a, Selector> {
type Output: Phone<'a, Phantom=Selector>;
fn new_phone(&self, ms: &'a str) -> Self::Output;
}
struct MyFactory;
impl<'a> Factory<'a, IPhonePhantom> for MyFactory {
type Output = IPhone<'a>;
fn new_phone(&self, ms: &'a str) -> IPhone<'a> {
IPhone {
my_str: ms
}
}
}
fn call_phone<Selector, F: for<'a> Factory<'a, Selector>>(f: F) {
for i in 0..10 {
let s = i.to_string();
let p = f.new_phone(&s);
p.call();
}
}
fn main() {
call_phone::<IPhonePhantom, _>(MyFactory);
}
The Phantom
associated type on the Phone
trait is not strictly necessary, it's only needed to tie the phone type to its phantom type and to make sure Factory
implementors don't lie. Phone
特性上的Phantom
相关类型并不是绝对必要的,只需要将电话类型与其幻像类型联系起来,并确保Factory
实现者不会撒谎。
Your problem is here: 你的问题在这里:
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) {
// Factory has a lifetime 'a ----------^
// that is at least as long as the scope of call_phone
for _ in 0..10 {
let s = String::new(); // s is born here
let p = f.new_phone(s.as_str());
// new reference ---^
// new_phone definition requires this to have
// the same lifetime 'a as the Factory
p.call();
}
// s is destroyed here, no references to s can
// still exist
} // F is still alive
One thing you can do is passing the &str
as a parameter to call_phone
, to make sure the reference lives as long as the function: 你可以做的一件事是将
&str
作为参数传递给call_phone
,以确保引用的存在时间与函数一样长:
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F, s: &'a str) {
for _ in 0..10 {
let p = f.new_phone(s);
p.call();
}
}
fn main() {
call_phone(IPhoneFactory, &"hello");
}
Another one is not working with references, but let your struct IPhone
own the String
另一个不使用引用,但让你的
struct IPhone
拥有String
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.