繁体   English   中英

在Rust中使用工厂模式时指定生命周期

[英]Specifying lifetimes when using the factory pattern in Rust

以下代码无法编译:

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);
}

我收到以下错误:

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();
                              ^

我希望能够有一个返回抽象类的工厂,但是当该类接受引用时,我无法弄清楚如何正确地指定生命周期。

你是对的:

没有理由只要工厂存在引用,它只需要工厂生成的对象就可以存活(工厂本身不存储对字符串的引用)。

但是call_phone上的call_phone说明了一些不同的东西

fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { ... }

该代码表示​​整个工厂只有一个生命周期 ,将用于每部手机 你想要不同的东西,你想说f对于任何一生来说都是一个好工厂:

fn call_phone<..., F: for<'a> Factory<'a, ...>>(f: F) { ... }

另一个问题是在Factory特征定义中:

trait Factory<'a, P: Phone> {
    fn new_phone(&self, ms: &'a str) -> P;
}

Pms生命周期没有任何关系。 特征定义允许返回的电话比字符串更长,这绝对不应该被IPhone实现! 因此,为了解决这个问题,我们在Phone特性中添加了一个生命周期参数:

trait Phone<'a> {
    fn call(&self);
}

但仍有一个问题。 我们不能写那个签名:

fn call_phone<P: ???, F: for<'a> Factory<'a, P<'a>>(f: F) { ... }

因为我们希望P不是一个类型,而是一个类型族(更准确地说,是一个lifetime → type构造函数)。 请记住,循环的每次迭代中的电话具有不同的类型(因为生命周期是类型的一部分,并且循环的不同迭代中的生命期是不同的)。

表示这样的签名的能力计划用于未来的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);
}

相关类型允许工厂只生产一种产品,这可能是你想要的。 如果您希望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);
}

Phone特性上的Phantom相关类型并不是绝对必要的,只需要将电话类型与其幻像类型联系起来,并确保Factory实现者不会撒谎。

你的问题在这里:

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

你可以做的一件事是将&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");
}

另一个不使用引用,但让你的struct IPhone拥有String

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM