Why do lifetimes differ when a function is called in a closure vs. being called directly in Rust?











up vote
11
down vote

favorite
3












In the following code example:



fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}

fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);

// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());

// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}


the last statement fails to compile with:



error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...


I'm wondering:




  1. what's happening that causes the difference in behaviour between the last two statements

  2. whether the first unwrap_or_else(|| default_values()) is the correct way of handling this, or whether there's a better pattern










share|improve this question


















  • 2




    I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
    – DanSnow
    Nov 6 at 11:48






  • 2




    Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
    – hellow
    Nov 6 at 12:02












  • Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
    – Stargateur
    Nov 6 at 12:50















up vote
11
down vote

favorite
3












In the following code example:



fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}

fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);

// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());

// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}


the last statement fails to compile with:



error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...


I'm wondering:




  1. what's happening that causes the difference in behaviour between the last two statements

  2. whether the first unwrap_or_else(|| default_values()) is the correct way of handling this, or whether there's a better pattern










share|improve this question


















  • 2




    I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
    – DanSnow
    Nov 6 at 11:48






  • 2




    Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
    – hellow
    Nov 6 at 12:02












  • Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
    – Stargateur
    Nov 6 at 12:50













up vote
11
down vote

favorite
3









up vote
11
down vote

favorite
3






3





In the following code example:



fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}

fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);

// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());

// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}


the last statement fails to compile with:



error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...


I'm wondering:




  1. what's happening that causes the difference in behaviour between the last two statements

  2. whether the first unwrap_or_else(|| default_values()) is the correct way of handling this, or whether there's a better pattern










share|improve this question













In the following code example:



fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}

fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);

// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());

// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}


the last statement fails to compile with:



error[E0597]: `values` does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...


I'm wondering:




  1. what's happening that causes the difference in behaviour between the last two statements

  2. whether the first unwrap_or_else(|| default_values()) is the correct way of handling this, or whether there's a better pattern







rust






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 6 at 11:36









Dave Challis

1,4651336




1,4651336








  • 2




    I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
    – DanSnow
    Nov 6 at 11:48






  • 2




    Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
    – hellow
    Nov 6 at 12:02












  • Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
    – Stargateur
    Nov 6 at 12:50














  • 2




    I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
    – DanSnow
    Nov 6 at 11:48






  • 2




    Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
    – hellow
    Nov 6 at 12:02












  • Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
    – Stargateur
    Nov 6 at 12:50








2




2




I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
– DanSnow
Nov 6 at 11:48




I don't understand why but I changed it to default_values as fn() -> &'static _ then it'll work.
– DanSnow
Nov 6 at 11:48




2




2




Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
– hellow
Nov 6 at 12:02






Nightly + feature(nll) gives an additional information: 'borrowed value does not live long enough, cast requires that values is borrowed for 'static'
– hellow
Nov 6 at 12:02














Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
– Stargateur
Nov 6 at 12:50




Look like the compile infer _v based on the return type of the function instead of infer _v based on the type of optional_value, and actually only as fn() -> _ is enough. That simply a case of "you need to help the compiler".
– Stargateur
Nov 6 at 12:50












2 Answers
2






active

oldest

votes

















up vote
5
down vote



accepted










This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.



When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).



As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.



Another solution is to make the type of default_values one that can implement the Fn trait you need:



fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}


Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.






share|improve this answer

















  • 1




    This still feels like a bug. Probably this is an unpolished edge of HRTB.
    – Peter Hall
    Nov 6 at 13:37






  • 1




    I agree, it does feel like a bug.
    – trentcl
    Nov 6 at 20:42


















up vote
2
down vote













There is no difference between a closure and a direct function call: It is just a matter of type inference.



closure that compiles:



let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});


closure that not compiles:



let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});


function that compiles:



let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);


function that not compiles:



let _v = unwrap_or_else(optional_values, default_values);


A litte bit of explanation



Consider this equivalent code:



fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}

fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}


the following snippet:



fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);

let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});

// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}


fails:



error[E0597]: `values` does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `values` is borrowed for `'static`
...
27 | }
| - `values` dropped here while still borrowed


Look from the monomorphization side: assuming that
the compiler infers that T resolves to the concrete type &'static [u32],
and supposing that the produced code is something like:



fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}


then the above monomorphization explains the error:



slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.



If you write:



let _v = unwrap_or_else(optional_values, || default_values());

// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});


It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.



Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53171108%2fwhy-do-lifetimes-differ-when-a-function-is-called-in-a-closure-vs-being-called%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    5
    down vote



    accepted










    This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.



    When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).



    As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.



    Another solution is to make the type of default_values one that can implement the Fn trait you need:



    fn default_values<'a>() -> &'a [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
    }


    Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.






    share|improve this answer

















    • 1




      This still feels like a bug. Probably this is an unpolished edge of HRTB.
      – Peter Hall
      Nov 6 at 13:37






    • 1




      I agree, it does feel like a bug.
      – trentcl
      Nov 6 at 20:42















    up vote
    5
    down vote



    accepted










    This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.



    When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).



    As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.



    Another solution is to make the type of default_values one that can implement the Fn trait you need:



    fn default_values<'a>() -> &'a [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
    }


    Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.






    share|improve this answer

















    • 1




      This still feels like a bug. Probably this is an unpolished edge of HRTB.
      – Peter Hall
      Nov 6 at 13:37






    • 1




      I agree, it does feel like a bug.
      – trentcl
      Nov 6 at 20:42













    up vote
    5
    down vote



    accepted







    up vote
    5
    down vote



    accepted






    This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.



    When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).



    As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.



    Another solution is to make the type of default_values one that can implement the Fn trait you need:



    fn default_values<'a>() -> &'a [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
    }


    Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.






    share|improve this answer












    This happens because default_values implements Fn() -> &'static [u32], but not for<'a> Fn() -> &'a [u32]. Traits are invariant, so you can't coerce "something that implements Fn() -> &'static [u32]" to "something that implements Fn() -> &'a [u32]" (for some 'a smaller than 'static), even though, logically speaking, default_values could satisfy both.



    When it's called in a closure, default_values() returns a &'static [u32], but it can be coerced immediately to a &'a u32, making the closure itself able to implement Fn() -> &'a [u32] (where the &'a is determined by the compiler).



    As for why adding as fn() -> &'static [u32] works, I assume the compiler can recognize that the function pointer type fn() -> &'static [u32] is capable of implementing Fn() -> &'a [u32] for any 'a. I'm not sure why it doesn't also do this for ordinary functions and closures; perhaps a future compiler version could be smart enough to allow the original code.



    Another solution is to make the type of default_values one that can implement the Fn trait you need:



    fn default_values<'a>() -> &'a [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
    }


    Instead of saying "this is a function that returns a 'static reference", the signature here says "this is a function that can return a reference of any lifetime". We know that "a reference of any lifetime" has to be a 'static reference, but the compiler sees the signatures as different because this one has an additional degree of freedom. This change is sufficient to make your original example compile.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 6 at 12:50









    trentcl

    6,05731234




    6,05731234








    • 1




      This still feels like a bug. Probably this is an unpolished edge of HRTB.
      – Peter Hall
      Nov 6 at 13:37






    • 1




      I agree, it does feel like a bug.
      – trentcl
      Nov 6 at 20:42














    • 1




      This still feels like a bug. Probably this is an unpolished edge of HRTB.
      – Peter Hall
      Nov 6 at 13:37






    • 1




      I agree, it does feel like a bug.
      – trentcl
      Nov 6 at 20:42








    1




    1




    This still feels like a bug. Probably this is an unpolished edge of HRTB.
    – Peter Hall
    Nov 6 at 13:37




    This still feels like a bug. Probably this is an unpolished edge of HRTB.
    – Peter Hall
    Nov 6 at 13:37




    1




    1




    I agree, it does feel like a bug.
    – trentcl
    Nov 6 at 20:42




    I agree, it does feel like a bug.
    – trentcl
    Nov 6 at 20:42












    up vote
    2
    down vote













    There is no difference between a closure and a direct function call: It is just a matter of type inference.



    closure that compiles:



    let _v = optional_values.unwrap_or_else(|| default_values());
    let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});


    closure that not compiles:



    let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});


    function that compiles:



    let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);


    function that not compiles:



    let _v = unwrap_or_else(optional_values, default_values);


    A litte bit of explanation



    Consider this equivalent code:



    fn default_values() -> &'static [u32] {
    static VALUES: [u32; 3] = [1, 2, 3];
    &VALUES
    }

    fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
    F: FnOnce() -> T, {
    match slf {
    Some(t) => t,
    None => f()
    }
    }


    the following snippet:



    fn main() {
    let values: [u32; 3] = [4, 5, 6];
    let optional_values: Option<&[u32]> = Some(&values);

    let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});

    // the above throws the same error of:
    //let _v = unwrap_or_else(optional_values, default_values);
    }


    fails:



    error[E0597]: `values` does not live long enough
    --> src/main.rs:18:48
    |
    18 | let optional_values: Option<&[u32]> = Some(&values);
    | ^^^^^^^
    | |
    | borrowed value does not live long enough
    | cast requires that `values` is borrowed for `'static`
    ...
    27 | }
    | - `values` dropped here while still borrowed


    Look from the monomorphization side: assuming that
    the compiler infers that T resolves to the concrete type &'static [u32],
    and supposing that the produced code is something like:



    fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
    f: fn() -> &'static [u32]) -> &'static [u32] {
    ...
    }


    then the above monomorphization explains the error:



    slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.



    If you write:



    let _v = unwrap_or_else(optional_values, || default_values());

    // the same, expliciting the return type:
    let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});


    It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.



    Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.






    share|improve this answer

























      up vote
      2
      down vote













      There is no difference between a closure and a direct function call: It is just a matter of type inference.



      closure that compiles:



      let _v = optional_values.unwrap_or_else(|| default_values());
      let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});


      closure that not compiles:



      let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});


      function that compiles:



      let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);


      function that not compiles:



      let _v = unwrap_or_else(optional_values, default_values);


      A litte bit of explanation



      Consider this equivalent code:



      fn default_values() -> &'static [u32] {
      static VALUES: [u32; 3] = [1, 2, 3];
      &VALUES
      }

      fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
      F: FnOnce() -> T, {
      match slf {
      Some(t) => t,
      None => f()
      }
      }


      the following snippet:



      fn main() {
      let values: [u32; 3] = [4, 5, 6];
      let optional_values: Option<&[u32]> = Some(&values);

      let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});

      // the above throws the same error of:
      //let _v = unwrap_or_else(optional_values, default_values);
      }


      fails:



      error[E0597]: `values` does not live long enough
      --> src/main.rs:18:48
      |
      18 | let optional_values: Option<&[u32]> = Some(&values);
      | ^^^^^^^
      | |
      | borrowed value does not live long enough
      | cast requires that `values` is borrowed for `'static`
      ...
      27 | }
      | - `values` dropped here while still borrowed


      Look from the monomorphization side: assuming that
      the compiler infers that T resolves to the concrete type &'static [u32],
      and supposing that the produced code is something like:



      fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
      f: fn() -> &'static [u32]) -> &'static [u32] {
      ...
      }


      then the above monomorphization explains the error:



      slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.



      If you write:



      let _v = unwrap_or_else(optional_values, || default_values());

      // the same, expliciting the return type:
      let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});


      It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.



      Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.






      share|improve this answer























        up vote
        2
        down vote










        up vote
        2
        down vote









        There is no difference between a closure and a direct function call: It is just a matter of type inference.



        closure that compiles:



        let _v = optional_values.unwrap_or_else(|| default_values());
        let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});


        closure that not compiles:



        let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});


        function that compiles:



        let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);


        function that not compiles:



        let _v = unwrap_or_else(optional_values, default_values);


        A litte bit of explanation



        Consider this equivalent code:



        fn default_values() -> &'static [u32] {
        static VALUES: [u32; 3] = [1, 2, 3];
        &VALUES
        }

        fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
        F: FnOnce() -> T, {
        match slf {
        Some(t) => t,
        None => f()
        }
        }


        the following snippet:



        fn main() {
        let values: [u32; 3] = [4, 5, 6];
        let optional_values: Option<&[u32]> = Some(&values);

        let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});

        // the above throws the same error of:
        //let _v = unwrap_or_else(optional_values, default_values);
        }


        fails:



        error[E0597]: `values` does not live long enough
        --> src/main.rs:18:48
        |
        18 | let optional_values: Option<&[u32]> = Some(&values);
        | ^^^^^^^
        | |
        | borrowed value does not live long enough
        | cast requires that `values` is borrowed for `'static`
        ...
        27 | }
        | - `values` dropped here while still borrowed


        Look from the monomorphization side: assuming that
        the compiler infers that T resolves to the concrete type &'static [u32],
        and supposing that the produced code is something like:



        fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
        f: fn() -> &'static [u32]) -> &'static [u32] {
        ...
        }


        then the above monomorphization explains the error:



        slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.



        If you write:



        let _v = unwrap_or_else(optional_values, || default_values());

        // the same, expliciting the return type:
        let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});


        It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.



        Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.






        share|improve this answer












        There is no difference between a closure and a direct function call: It is just a matter of type inference.



        closure that compiles:



        let _v = optional_values.unwrap_or_else(|| default_values());
        let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});


        closure that not compiles:



        let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});


        function that compiles:



        let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);


        function that not compiles:



        let _v = unwrap_or_else(optional_values, default_values);


        A litte bit of explanation



        Consider this equivalent code:



        fn default_values() -> &'static [u32] {
        static VALUES: [u32; 3] = [1, 2, 3];
        &VALUES
        }

        fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
        F: FnOnce() -> T, {
        match slf {
        Some(t) => t,
        None => f()
        }
        }


        the following snippet:



        fn main() {
        let values: [u32; 3] = [4, 5, 6];
        let optional_values: Option<&[u32]> = Some(&values);

        let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});

        // the above throws the same error of:
        //let _v = unwrap_or_else(optional_values, default_values);
        }


        fails:



        error[E0597]: `values` does not live long enough
        --> src/main.rs:18:48
        |
        18 | let optional_values: Option<&[u32]> = Some(&values);
        | ^^^^^^^
        | |
        | borrowed value does not live long enough
        | cast requires that `values` is borrowed for `'static`
        ...
        27 | }
        | - `values` dropped here while still borrowed


        Look from the monomorphization side: assuming that
        the compiler infers that T resolves to the concrete type &'static [u32],
        and supposing that the produced code is something like:



        fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
        f: fn() -> &'static [u32]) -> &'static [u32] {
        ...
        }


        then the above monomorphization explains the error:



        slf value is optional_values: an Option<&'a [u32]> that does not live enough and clearly cannot be cast because it does not satisfies the 'static lifetime requirement.



        If you write:



        let _v = unwrap_or_else(optional_values, || default_values());

        // the same, expliciting the return type:
        let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});


        It compiles: now the lifetime of the return type is compatible with the optional_values lifetime.



        Finally, I'm not able to explain why, but the evidence shows that the cast as fn() -> &'static _ helps the compiler to be sure that decoupling lifetimes bound to optional_values and default_values is safe.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 7 at 22:44









        attdona

        2,83411118




        2,83411118






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53171108%2fwhy-do-lifetimes-differ-when-a-function-is-called-in-a-closure-vs-being-called%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Academy of Television Arts & Sciences

            L'Équipe

            1995 France bombings