处理`Into`泛型时的Rust生命周期问题

时间:2017-05-24 17:14:26

标签: generics rust lifetime

感谢您纠正我提问的方式。我做了几个修改以使代码可编译。

use std::marker::PhantomData;

struct Brace {
    x: i32,
}

impl Brace {
    fn transform(&self, n: i32) -> Devil {
        Devil {
            hp: self.x + n,
            weapon: None,
        }
    }
}

struct Bar<'a> {
    tasty: &'a str,
}

struct Foo<'a, B>
    where B: 'a + Into<Bar<'a>>
{
    brace: Brace,
    buz: Option<B>, // buz is of generic type B, and is able to be turned into bar.
    phantom: PhantomData<&'a B>, // A marker that is used to resolve 'unused lifetime parameter a'
}

impl<'a, B: Into<Bar<'a>>> Foo<'a, B> {
    fn transform_and_arm(self) {
        // line B
        let brace1: Brace = self.brace;
        let mut devil: Devil = brace1.transform(12345); // line A
        let buz = self.buz.unwrap();
        // Before this line, it passes the compiler.
        // Uncommenting the following line causes compiler to argue that the brace1 at line A doesn't live long enough. It says that borrowed value must be valid for the lifetime 'a as defined on the body at line B, but the borrowed value only lives until line C.
        // devil = devil.arm(buz);
        // Although adding the above line fails, making the weapon directly won't cause the compiler to complain.
        // Uncommenting the following line passes compiler.
        // let weapon = buz.into();

        // The compiler stops the devil from arming itself before I even try to write the following line.
        // devil.slay_the_world();
    } // line C
}

struct Devil<'a> {
    hp: i32,
    weapon: Option<Bar<'a>>,
}

impl<'a> Devil<'a> {
    fn arm<B: Into<Bar<'a>>>(mut self, biu: B) -> Devil<'a> {
        self.weapon = Some(biu.into());
        self
    }

    fn slay_the_world(self) {
        unimplemented!()
    }
}

transform_and_arm()方法旨在通过取消Foobrace来使用buz的实例。它调用brace.transform()使brace成为Devil。它通过用buz.unwrap()喂养魔鬼来加强魔鬼。

问题是,调用let weapon = buz.into();是合法的,而调用devil = devil.arm(buz);会导致生命周期问题。

似乎这个问题与生命周期有很大关系。如果没有魔鬼,那么所有这些问题都会消失。

1 个答案:

答案 0 :(得分:0)

编辑我之前的回答并没有很好地解释这个问题。这是另一种尝试。

您遇到此问题是因为transform存在缺陷。为了找出原因,让我们看看编译器为transform推断的生命周期:

fn transform<'a>(&'a self, n: i32) -> Devil<'a>

我们可以看到编译器决定给返回的Devil提供与传递给函数的self的引用相同的生命周期。

现在,让我们看一下Devil

struct Devil<'a> {
    hp: i32,
    weapon: Option<Bar<'a>>,
}

我们可以看到Devil的生命周期参数与其weapon相关联。

回顾transform,但是......

fn transform<'a>(&'a self, n: i32) -> Devil<'a> {
    Devil {
        hp: self.x + n,
        weapon: None,
    }
}

...很明显,weaponDevil的生命周期参数的唯一目的,与self的引用无关,所以&selfDevil的生命周期应该彼此无关

这会在以后为Devil分配实际武器时导致问题,因为我们可能希望Devil weapon具有不同的生命周期,并且没有理由我们不应该这样做。

此外,transform的当前实施禁止返回的Devil超过对self的引用,并且不一定是这种情况。

要解决此问题,请明确注释transform以明确&self并且返回的Devil具有无关的生命周期:

fn transform<'a, 'b>(&'a self, n: i32) -> Devil<'b>