Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can't return fmt::Arguments<'a> from &'a T?

Tags:

Based on my understanding of lifetimes, if the caller of a function specifies a lifetime on a parameter, I can return a type with that lifetime.

This works, even with elision:

pub fn substr(s: &str) -> &str {
    &s[0..1]
}

pub fn substr_ex<'a>(s: &'a str) -> &'a str {
    &s[0..1]
}

But this doesn't:

use std::fmt::Arguments;

pub fn as_format_arg<'a, T: 'a + ?Sized + Debug>(t: &'a T) -> Arguments<'a> {
    format_args!("{:?}", t)
}
error: borrowed value does not live long enough
  --> <anon>:16:18
   |
16 |     format_args!("{:?}", t)
   |                  ^^^^^^ does not live long enough
17 | }
   | - temporary value only lives until here
   |
   = note: borrowed value must be valid for the lifetime 'a as defined on unknown free region bounded by scope CodeExtent(38/CallSiteScope { fn_id: NodeId(42), body_id: NodeId(92) })...

error: `t` does not live long enough
  --> <anon>:16:26
   |
16 |     format_args!("{:?}", t)
   |                          ^ does not live long enough
17 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the lifetime 'a as defined on unknown free region bounded by scope CodeExtent(38/CallSiteScope { fn_id: NodeId(42), body_id: NodeId(92) })...

Is this a bug? Or am I misunderstanding lifetimes?

Playpen: https://play.rust-lang.org/?gist=5a7cb4c917b38e012f20c771893f8b3b&version=nightly

like image 691
kdy Avatar asked Dec 21 '16 13:12

kdy


1 Answers

To understand what's happening, let's look at the macro-expanded version:

fn as_format_arg<'a, T: 'a + ?Sized + Debug>(t: &'a T) -> Arguments<'a> {
    ::std::fmt::Arguments::new_v1(
        {
            static __STATIC_FMTSTR: &'static [&'static str] = &[""];
            __STATIC_FMTSTR
        },
        &match (&t,) {
            (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)],
        },
    )
}

This helps explain the first error:

error: borrowed value does not live long enough
  --> src/main.rs:9:36
   |
9  |                                   &match (&t,) {
   |                                    ^ temporary value created here
...
15 | }
   | - temporary value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the block at 4:72...
  --> src/main.rs:4:73
   |
4  | fn as_format_arg<'a, T: 'a + ?Sized + Debug>(t: &'a T) -> Arguments<'a> {
   |                                                                         ^

Specifically, an ArgumentV1 is being created on the stack and a reference is being taken of it. You cannot return that reference from the function.

The second error:

error: `t` does not live long enough
  --> src/main.rs:9:44
   |
9  |                                   &match (&t,) {
   |                                            ^ does not live long enough
...
15 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the block at 4:72...
  --> src/main.rs:4:73
   |
4  | fn as_format_arg<'a, T: 'a + ?Sized + Debug>(t: &'a T) -> Arguments<'a> {
   |                                                                         ^

Note that the format! family of macros doesn't take their arguments by value; they automatically insert a reference. You wouldn't want println! to take ownership of your value!.

This means that the printed value is actually a &&'a T - a reference to the stack-allocated t value! Again, you cannot return a reference to something allocated on the stack.

if the caller of a function specifies a lifetime on a parameter, I can return a type with that lifetime.

This is halfway true. You can return only return a piece of that input parameter. You cannot create a completely new value and return it with that lifetime.

like image 85
Shepmaster Avatar answered Sep 22 '22 16:09

Shepmaster