在S <t>上使用通用特征强制我使用T outlive S.

时间:2017-08-26 19:57:20

标签: rust lifetime ownership-semantics

煮沸后的问题如下:

use std::marker::PhantomData;

struct WorldState<'a> {
    state: &'a f64,
}

trait CalculateWorldState<T> {
    fn state_value(&mut self, input: &T) -> f64;
}


trait LearningAlgorithm<T> {
    fn print_learning_information(&self, &T);
}

struct EvolutionaryAlgorithm<F, T>
where
    F: CalculateWorldState<T>,
{
    //I need this since I only use T as a method parameter, I do not save it anywhere
    //T are different ways to represent the current worldstate and are
    //short-lived (new ones generated every frame)
    _p_: PhantomData<T>,
    //I don't actually need this one in the real example since I have
    //an instatiated version of type CalculateWorldState saved in the
    //struct but I use phantomdata for simplicity of the example
    _p: PhantomData<F>,
}

impl<F, T> LearningAlgorithm<T> for EvolutionaryAlgorithm<F, T>
where
    F: CalculateWorldState<T>,
{
    fn print_learning_information(&self, input: &T) {
        println!("My learning goes splendid!");
        //do something with &T by calling the object of type
        //CalculateWorldState which we have saved somewhere, but do
        //not save the &T reference anywhere, just look at it
    }
}

struct WorldIsInGoodState {}

impl<'a> CalculateWorldState<WorldState<'a>> for WorldIsInGoodState {
    fn state_value(&mut self, input: &WorldState) -> f64 {
        100.
    }
}

fn main() {
    let mut a: Box<LearningAlgorithm<WorldState>> =
        Box::new(EvolutionaryAlgorithm::<WorldIsInGoodState, WorldState> {
            _p: PhantomData,
            _p_: PhantomData,
        });
    {
        let state = WorldState { state: &5. };
        a.print_learning_information(&state);
    }
}

Playground

以上代码无法编译:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:59:5
   |
57 |         let state = WorldState { state: &5. };
   |                                          -- temporary value created here
58 |         a.print_learning_information(&state);
59 |     }
   |     ^ temporary value dropped here while still borrowed
60 | }
   | - temporary value needs to live until here

WorldState<'a>是一种非常短暂的数据类型(每帧一个),而LearningAlgorithm是一种非常长寿的数据类型(多个游戏)。但是我实现这个东西的方式,Rust非常渴望相信,我传递给WorldState的每个print_learning_information都必须比LearningAlgorithm更长。

我做错了什么?怎么可以处理这个呢?

我不想做的一些事情:

  • WorldState包含正常状态(因为实际上它包含一些向量,而不是f64,我不想将它们复制到WorldState结构中通过每个球员自己的世界观点)
  • 刚刚退出这个项目并开始一个新项目(你们都知道,在投入一些时间后,你们不想把所有工作都扔掉)

1 个答案:

答案 0 :(得分:0)

您的问题可归结为

struct WorldState<'a> {
    state: &'a f64,
}

trait LearningAlgorithm<T> {
    fn print_learning_information(&self, &T);
}

struct EvolutionaryAlgorithm();

impl<T> LearningAlgorithm<T> for EvolutionaryAlgorithm
{
    fn print_learning_information(&self, input: &T) {
    }
}

fn main() {
    // scope a
    let mut a: Box<LearningAlgorithm<WorldState>> =
        Box::new(EvolutionaryAlgorithm());
    { // scope b
        let val = 5.;
        let state = WorldState { state: &val };
        a.print_learning_information(&state);
    }
}

请注意WorldState是一个类型构造函数,而不是具体类型。 Lifetime elision允许您编写Box<LearningAlgorithm<WorldState>>而没有为WorldState显式指定的生命周期参数,但它只是意味着编译器选择了一些适当的生命周期参数。

在这种情况下,为WorldState选择的生命周期为scope a,因此a的类型为Box<LearningAlgorithm<WorldState<'scope_a>>>。因此,state应该具有WorldState<'scope_a>类型,并且其包含的引用应该对scope a有效,但引用指向的值仅存在于scope b

您需要支持更高级别的类型才能使您的示例按原样运行,但Rust不提供它。

最简单的解决方案是通过用WorldState替换引用来摆脱Rc的生命周期参数。也许有人会提出更好的解决方案。