How do I specify a lifetime that is dependent on the borrowed binding of a closure in a separate type?

Kites

I have two types: Lexer and SFunction.

SFunction stands for stateful function and is definined like so:

struct SFunction {
    f: Option<Box<FnMut() -> SFunction>>, 
}

The important part is that any SFunction references a closure that returns an SFunction.

Now I want to have these functions carry state by each affecting the same Lexer. This means that each of these SFunctions has to have a lifetime that depends on a specific Lexer.

Here's some more code if you want to get more of a sense of what I'm doing with this:

impl Lexer {
    fn lex(&mut self) {
        self.sfunction(Lexer::lexNormal).call()
    }

    fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {

        SFunction::new(Box::new(|| f(self)))
        // SFunction { f: Some(Box::new(move ||f(self))) }
    }

    fn lexNormal(&mut self) -> SFunction {
        return SFunction::empty()
    }
}

(Here’s a full version of the code in the Rust playground.)

How do I specify this lifetime requirement in the code?

The compiler errors I'm getting say "cannot infer an appropriate lifetime for capture of self by closure due to conflicting requirements". I'm pretty sure the "conflicting requirements" here is that a Box type assumes the lifetime to be 'static. I could do something like Box<FnMut() -> SFunction + 'a> where 'a is a lifetime defined by the Lexer it depends upon, but I'm not sure how to define such an 'a.

Thanks for your help!

Shepmaster

The problem is in this line:

SFunction::new(Box::new(|| f(self)))

Here, self is a reference to a Lexer, but there's no guarantee that the lexer will live long enough. In fact, it needs to live for the 'static lifetime! Without a lifetime specified, a boxed trait object will use the 'static lifetime. Said in code, these two declarations are equivalent:

<Box<FnMut() -> SFunction>>
<Box<FnMut() -> SFunction> + 'static>

And you can make your code compile (in an unsatisfactory way) by restricting it to accept only references that will live for the 'static lifetime:

fn lex(&'static mut self) {
    self.sfunction(Lexer::lex_normal).call()
}

fn sfunction(&'static mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
    SFunction::new(Box::new(move || f(self)))
}

Of course, it's very doubtful that you will have a Lexer with the static lifetime, as that would mean that it's lexing static data, which wouldn't be very useful. That means we need to include lifetimes in your trait object... as you suggested.

Ultimately what helped to see the problem was to restructure your closure a bit:

fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
    SFunction::new(Box::new(move || {
        // f(self)
        let s2 = self;
        let f2 = f;
        f2(s2)
    }))
}

Compiling this produces an error that points to what seems to be the real problem:

<anon>:31:22: 31:26 error: cannot move out of captured outer variable in an `FnMut` closure [E0507]
<anon>:31             let s2 = self;
                               ^~~~
<anon>:31:17: 31:19 note: attempting to move value to here
<anon>:31             let s2 = self;
                          ^~
<anon>:31:17: 31:19 help: to prevent the move, use `ref s2` or `ref mut s2` to capture value by reference

I believe this is because a FnMut closure may be called multiple times, which would mean that the reference enclosed in the closure would need to be copied around, which would be bad news as &mut references should be unique.

All together, this code works:

struct SFunction<'a> {
    f: Option<Box<FnOnce() -> SFunction<'a> + 'a>>, 
}

impl<'a> SFunction<'a> {
    fn new(f: Box<FnOnce() -> SFunction<'a> + 'a>) -> SFunction<'a> {
        SFunction {
            f: Some(f),
        }
    }

    fn empty() -> SFunction<'a> {
        SFunction {
            f: None,
        }
    }

    fn call(self) { }
}

struct Lexer;

impl Lexer {
    fn lex(&mut self) {
        self.sfunction(Lexer::lex_normal).call()
    }

    fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
        SFunction::new(Box::new(move || f(self)))
    }

    fn lex_normal<'z>(&'z mut self) -> SFunction<'z> {
        SFunction::empty()
    }
}

fn main() {
    let mut l = Lexer;
    l.lex()
}

I hope my explanation is right and that the changed code still suits your use case!

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How do I specify a closure that takes a reference and returns any type implementing a trait with the same lifetime as the reference?

How do I specify lifetime parameters in an associated type?

How do I specify the lifetime of an AsRef?

How do I solve "lifetime of reference outlives lifetime of borrowed content" in the following context?

How do I specify the lifetime for the associated type of an iterator that refers to itself but does not mutate itself?

How do I specify @param @return for object type in Google Closure Compiler?

How do I specify the lifetime of a field as a combination of other fields?

How do I handle an FFI unsized type that could be owned or borrowed?

How to specify lifetime bounds for a closure involving references to intermediate local variables?

How do I declare the lifetime parameters for a type which contains a reference to a type with a lifetime parameter?

How do I specify the compression type in zlib?

How do I specify the correct return type?

How do I return a tuple with a borrowed value?

How do I specify DataContext (ViewModel) type to get design-time binding checking in XAML editor without creating a ViewModel object?

How do I separate an enum type?

Lifetime specification for closure return type

How can I solve conflicting lifetime requirements for a &PathBuf passed to a closure?

How do I "generify" a closure type alias in Swift?

How do I specify that the return type will be the same as the input type?

How do I specify the type of a prop to refer to the type of another prop?

How do I specify a type object that must be of another type?

Specify lifetime for enum as return type

In Swift, how do I explicitly specify a return value for map with anonymous closure arguments?

How to declare a lifetime for a closure argument?

How do I combine a Controlled Lifetime relationship type (i.e. Owned<T>) with a delegate factory?

How do I bound a generic type with a trait that requires a lifetime parameter if I create the reference inside the function?

How do I solve this with closure?

How do I specify a file type for importing .obj files?

How do I specify a generic type where one of the types is not needed?

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    pump.io port in URL

  5. 5

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  6. 6

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  7. 7

    Do Idle Snowflake Connections Use Cloud Services Credits?

  8. 8

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

  9. 9

    Binding element 'string' implicitly has an 'any' type

  10. 10

    BigQuery - concatenate ignoring NULL

  11. 11

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  12. 12

    In Skype, how to block "User requests your details"?

  13. 13

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  14. 14

    Pandas - check if dataframe has negative value in any column

  15. 15

    flutter: dropdown item programmatically unselect problem

  16. 16

    Generate random UUIDv4 with Elm

  17. 17

    Is it possible to Redo commits removed by GitHub Desktop's Undo on a Mac?

  18. 18

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  19. 19

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  20. 20

    EXCEL: Find sum of values in one column with criteria from other column

  21. 21

    How to use merge windows unallocated space into Ubuntu using GParted?

HotTag

Archive