Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store data with lifetimes in world #320

Open
sdupille opened this issue Jan 27, 2024 · 1 comment
Open

Store data with lifetimes in world #320

sdupille opened this issue Jan 27, 2024 · 1 comment
Assignees
Labels
k::api Related to API (application interface) k::design Related to overall design and/or architecture question Further information is requested

Comments

@sdupille
Copy link

Hey there ! I wrote a lib I want to test with cucumber. My lib use some struct that has some lifetimes, like this :

pub struct Intersection<'a> {
    time: f64,
    object: &'a Shape,
}

And I need to store these kinds of values in cucumber world. So, obvious solution was to create a world object with lifetime, like this :

#[derive(Debug)]
pub enum WorldItem<'a> {
    Tuple(Tuple),
    Double(f64),
    Color(Color),
    Canvas(Canvas),
    PPM(String),
    Matrix(Matrix),
    Ray(Ray),
    Shape(Shape),
    Intersect(Intersection<'a>),
    Interset(Intersections<'a>),
    Hit(Option<Intersection<'a>>),
    Light(PointLight),
    Material(Material),
}

#[derive(Debug, World)]
#[world(init = Self::new)]
pub struct CucumberWorld<'a> {
    all_entries: HashMap<String, WorldItem<'a>>,
}

But it doesn't work, and the message is weird :

error[E0106]: missing lifetime specifier
  --> tests/world/mod.rs:26:12
   |
26 | pub struct CucumberWorld<'a> {
   |            ^^^^^^^^^^^^^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
26 | pub struct CucumberWorld<'a><'a><'a> {
   |                         ++++

I also have a bunch of errors like this :

error: `impl` item signature doesn't match `trait` item signature
  --> tests/world/mod.rs:24:17
   |
24 | #[derive(Debug, World)]
   |                 ^^^^^ found `fn(&'1 CucumberThenCucumberWorld) -> (cucumber::step::Location, fn() -> Regex, for<'a> fn(&'a mut world::CucumberWorld<'1>, cucumber::step::Context) -> Pin<Box<(dyn futures::Future<Output = ()> + 'a)>>)`
   |
  ::: /Users/averell/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cucumber-0.20.2/src/codegen.rs:58:5
   |
58 |     fn inner(&self) -> (step::Location, LazyRegex, Step<W>);
   |     -------------------------------------------------------- expected `fn(&'1 CucumberThenCucumberWorld) -> (cucumber::step::Location, fn() -> Regex, for<'a> fn(&'a mut world::CucumberWorld<'a>, cucumber::step::Context) -> Pin<Box<(dyn futures::Future<Output = ()> + 'a)>>)`
   |
   = note: expected signature `fn(&'1 CucumberThenCucumberWorld) -> (cucumber::step::Location, fn() -> Regex, for<'a> fn(&'a mut world::CucumberWorld<'a>, cucumber::step::Context) -> Pin<Box<(dyn futures::Future<Output = ()> + 'a)>>)`
              found signature `fn(&'1 CucumberThenCucumberWorld) -> (cucumber::step::Location, fn() -> Regex, for<'a> fn(&'a mut world::CucumberWorld<'1>, cucumber::step::Context) -> Pin<Box<(dyn futures::Future<Output = ()> + 'a)>>)`
   = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
   = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output
   = note: this error originates in the derive macro `World` (in Nightly builds, run with -Z macro-backtrace for more info)

How can I create a world with lifetime ? Is there a way ? If not, any workaround ?

@tyranron tyranron added question Further information is requested k::api Related to API (application interface) k::design Related to overall design and/or architecture labels Jan 31, 2024
@tyranron tyranron self-assigned this Jan 31, 2024
@tyranron
Copy link
Member

tyranron commented Jan 31, 2024

@sdupille while we technically can make World macros desugaring supporting lifetimes in World to some extent, it's still not applicable by the whole design.

Let's leave concrete types for a moment, and look at the bigger picture here:

  • A fresh World instance is created by the framework for each scenario.
  • The whole lifecycle of the World is controlled by the framework. For the user code (step functions) there is no control over it. The framework decides when to destroy or where to pass the World (and it's not something deterministic).
  • Giving the World a lifetime parameter, means that it's not self-contained anymore, and depends on a lifespan of some other object.
  • Clearly, inside our user code (step functions) we only can create objects for the lifespan of the step execution.
  • Thus, we cannot make the World, which has the larger lifespan and is controlled by the framework, to refer/depend on an object living in a shorter lifespan of the step function.
  • The only stuff the World can refer to is either some global data (meaning 'static usage), or itself (self-referential stuff).

So, there can be several ways to deal with your problem:

  1. Either put the source the Intersection<'a>/WorldItem<'a> point to into the World too, making it self-referential. Crates like owning_ref or ouroboros should help here.
  2. Or, for tests, provide an owning variant of your Intersection<'a> type (i.e. IntersectionOwned) and use it.
  3. Or, if you're absolutely-absolutely-absolutely sure that the source, the Intersection<'a> points to, overlives the World, created by the framework on demand and being Droped who-knows-where (may be at the end of the program), you may unsafe { mem::transmute() } lifetime into a 'static one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
k::api Related to API (application interface) k::design Related to overall design and/or architecture question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants