# Request for a smarter lifetime: format!(...).as\_str() should be valid

**URL:** <https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423>\
**Category:** language design\
**Created:** [May 25, 2020, 4:21am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423 "2020-05-25T04:21:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)\
**Post date:** [May 25, 2020, 4:21am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/1 "2020-05-25T04:21:17Z")

</div>

## Problem

I often find myself write this pattern of code:

```rust
// verbose code
let text: String = expression_that_creates_string;
do_something(text.as_str());

```

The above code could have been shortened (see below) if not for lifetime restrictions.

```rust
// shortened code
do_something(expression_that_creates_string.as_str());
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: live not long enough

```

## Request

"shortened code" above should be transformed into "verbose code" by the compiler.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [May 25, 2020, 4:54am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/2 "2020-05-25T04:54:42Z")

</div>

The shortened code is equivalent to

```rust
do_something({
    let _1 = expression_that_creates_string;
    _1.as_str()
})

```

which doesn't work because `_1` is dropped at the end of the block. To make it work, the compiler could implicitly change it to

```rust
let _1;
do_something({
    _1 = expression_that_creates_string;
    _1.as_str()
})

```

(this would actually happen in the MIR)

For backwards compatibility, the compiler can only do this in situations that would otherwise fail to compile.

I'm concerned that this might add more complexity to the language, which makes it more ergonomic but also more difficult to comprehend. With this proposed feature, it's no longer obvious where a value is dropped.

---

<div class="post-metadata">

**Author:** ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)\
**Post date:** [May 25, 2020, 5:10am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/3 "2020-05-25T05:10:26Z")

</div>

If `_1` is **only** dropped at the end of a **function** or a **closure** , then the behavior would be consistent. I don't think there is any gain in dropping at expression/block level.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [May 25, 2020, 6:24am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/4 "2020-05-25T06:24:56Z")

</div>

Side note: if you expect that you'll operate with the result of formatting frequently, consider making your functions accept any `T: Display` or `T: ToString` instead of `&str`. In this manner you could pass the result of `format_args!(…)` directly, without having to allocate an intermediate string.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [May 25, 2020, 6:31am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/5 "2020-05-25T06:31:01Z")

</div>

You don't want to take something like `ToString`, because then you have to convert a `&str` to a `String` before you can use it, which allocates and copies.

Instead, take an `AsRef<str>`, which lets you take a `String` or `&str` and use it directly.

Alternatively, even if you take a `&str`, the caller _can_ pass `&format!(...)` or `format!(...).as_str()`, as long as the lifetime doesn't need to last longer than the call to `do_something`. The compiler already extends the lifetime of temporaries to the end of the statement.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [May 25, 2020, 6:40am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/6 "2020-05-25T06:40:08Z")

</div>

There are several reasons to drop values as early as possible, and not just at the end of a function, for example:

- freeing heap memory as early as possible reduces the total memory usage
- _not_ dropping a MutexGuard or RefMut can cause deadlocks or panics.

---

<div class="post-metadata">

**Author:** ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)\
**Post date:** [May 25, 2020, 6:46am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/7 "2020-05-25T06:46:51Z")

</div>

> [@Aloso](#):
>
> freeing heap memory as early as possible reduces the total memory usage

This can be achieved by looking up last reference to a variable (inside the scope of a function), which would allow dropping even sooner than end-of-block.

> [@Aloso](#):
>
> _not_ dropping a MutexGuard or RefMut can cause deadlocks or panics.

The above block is only called once (unlike functions, closure, and body of a loop).

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 25, 2020, 6:53am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/8 "2020-05-25T06:53:20Z")

</div>

Drop order is stable, and rustc has explicitly committed to not changing drop order.

Dropping (nontrivial drop) items early like it looks like you suggest would be a destructively huge breaking change.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [May 25, 2020, 7:02am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/9 "2020-05-25T07:02:31Z")

</div>

> [@KSXGitHub](#):
>
> If `_1` is **only** dropped at the end of a **function** or a **closure** , then the behavior would be consistent.

The problem becomes evident in this example:

```rust
fn deadlock(mutex: Mutex<Foo>) {
    do_something(mutex.lock().unwrap().foo());
    do_something(mutex.lock().unwrap().bar());
}

```

If the call to `foo()` borrows the MutexGuard, and the compiler therefore drops it only at the end of the function, it will result in a deadlock (I didn't test it, please correct me if I'm wrong).

> [@CAD97](#):
>
> Dropping (nontrivial drop) items early like it looks like you suggest would be a destructively huge breaking change.

I'm not sure which comment you are refering to. I don't think the drop order should be changed.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [May 25, 2020, 8:32am UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/10 "2020-05-25T08:32:17Z")

</div>

Yes, thinking about it, I should have written `Display` only (and your comment about lifetimes also applies indeed, fair enough).

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [May 25, 2020, 12:34pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/11 "2020-05-25T12:34:06Z")

</div>

This works already:

```rust
do_something(&expression_that_creates_string());

```

---

<div class="post-metadata">

**Author:** ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)\
**Post date:** [May 25, 2020, 1:08pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/12 "2020-05-25T13:08:48Z")

</div>

For this particular example, yes.

What about `a.as_ref().foo().as_ref().bar()`?

---

<div class="post-metadata">

**Author:** ![daboross](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daboross/32/12645_2.png) [@daboross](https://internals.rust-lang.org/u/daboross)\
**Post date:** [May 25, 2020, 2:54pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/13 "2020-05-25T14:54:04Z")

</div>

`&new_string()` should be valid in the exact same situations where `new_string().as_ref()` is - neither has special rules. If I recall correctly, the temporary will stay alive for the entire statement, but not longer than that.

By that I mean, as long as `do_something` has no particular lifetime requirements on the `&str` it consumes, even this is valid:

```rust
do_something(format!("a {} string", "new").as_str());

```

By that I mean that this compiles in playground:

```rust
fn do_something(v: &str) {}

fn main() {
    do_something(format!("a {} string", "new").as_str());
}

```

So if there were extra restrictions on the lifetime `do_something` can accept that would cause the example in the OP to fail, then `do_something(&format!(""))` would fail as well.

* * *

Responding to the OP, I think something _similar_ to what you're suggesting already happens - it just isn't as broad. Rather than transforming your code into

```rust
let text: String = expression_that_creates_string;
do_something(text.as_str());

```

Rust transforms it into

```rust
{
    let text: String = expression_that_creates_string;
    do_something(text.as_str())
};

```

The temporary _is_ guaranteed to live as long as the statement, but it lives only that long, not as long as the entire block the statement is in.

As others have stated, this is preferable in general, and allows much nicer usage with mutexes and such. For instance, how long would you expect this code to hold the mutex for?

```rust
let x = calc_a;
mutex.lock().insert(x);
let y = calc_b;

```

Under rust's current rules, `mutex` is unlocked directly before the `let y = ...` line. If we went with your suggestion instead, then `mutex` would remain locked through `calc_b` - I'd argue the confusion in this case wouldn't be worth the extra simplicity when dealing with things like strings.

There could be a way around that - only extending the drop if the lifetime required it. I think this would probably also be bad, though, since it'd become even harder to analyze the behavior of any particular code segment. In addition, all valid rust code can currently be compiled without understanding lifetime bounds - breaking that for ergonomics probably wouldn't be a good idea?

Hope that wasn't overly harsh! I think looking for ways to improve the way rust handles things is good, but there are a lot of pitfalls when dealing with drop rules. Plus, as CAD97 pointed out, these rules are stable. Changing the drop order so that intermediate values are kept longer could and would break code.

---

<div class="post-metadata">

**Author:** ![mbrubeck](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mbrubeck/32/174_2.png) [@mbrubeck](https://internals.rust-lang.org/u/mbrubeck)\
**Post date:** [May 25, 2020, 3:45pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/14 "2020-05-25T15:45:16Z")

</div>

There is an [accepted RFC](https://github.com/rust-lang/rfcs/blob/master/text/0066-better-temporary-lifetimes.md) for this, though it is extremely old, somewhat vague, and never implemented.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [May 27, 2020, 1:29pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/15 "2020-05-27T13:29:46Z")

</div>

> [@daboross](#):
>
> Plus, as CAD97 pointed out, these rules are stable.

It wouldn't be a breaking change to relax only cases which currently lead to a compilation error.

---

<div class="post-metadata">

**Author:** ![jjpe](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jjpe/32/9779_2.png) [@jjpe](https://internals.rust-lang.org/u/jjpe)\
**Post date:** [May 27, 2020, 1:38pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/16 "2020-05-27T13:38:24Z")

</div>

Off topic, but isn't that symptomatic of a breakdown somewhere in the RFC process?

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [May 27, 2020, 1:39pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/17 "2020-05-27T13:39:18Z")

</div>

Once upon a time there was a proposal to add eager drop to Rust. If this was generalized to an opt-in "side-effect-free drop", then it would give freedom to the compiler to shorten — or extend — lifetime of such objects without risk of interfering with mutexes or other objects relying on the stable drop order.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [May 27, 2020, 4:27pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/18 "2020-05-27T16:27:59Z")

</div>

> [@Aloso](#):
>
> The shortened code is equivalent to
> 
> ```rust
> do_something({
> let _1 = expression_that_creates_string;
> _1.as_str()
> })
> 
> ```

This gets into complicated edge cases, so I don't think it's that simple. I think it's actually more like

```rust
do_something(match expression_that_creates_string { x => x.as_str() })

```

Though I'm bad at remembering the exact rules for how that's different.

---

<div class="post-metadata">

**Author:** ![mbrubeck](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mbrubeck/32/174_2.png) [@mbrubeck](https://internals.rust-lang.org/u/mbrubeck)\
**Post date:** [May 27, 2020, 4:51pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/19 "2020-05-27T16:51:55Z")

</div>

> [@daboross](#):
>
> `&new_string()` should be valid in the exact same situations where `new_string().as_ref()` is - neither has special rules. If I recall correctly, the temporary will stay alive for the entire statement, but not longer than that.

This is not true, by the way. There _are_ special rules to extend temporary lifetimes. They currently apply to the first expression but not the second. RFC 66, when implemented, will extend them to both.

This compiles today:

```rust
fn new_string() -> String {
    "hello".into()
}

let x: &str = &new_string();
dbg!(x);

```

This is an error today, but RFC 66 makes it valid code:

```rust
let x: &str = new_string().as_ref();
dbg!(x);

```

[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=efb7c1e18c6b89c8cf9fd3ac5b5dd095)

---

<div class="post-metadata">

**Author:** ![daboross](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daboross/32/12645_2.png) [@daboross](https://internals.rust-lang.org/u/daboross)\
**Post date:** [May 27, 2020, 11:28pm UTC](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423/20 "2020-05-27T23:28:46Z")

</div>

Thank you for correcting me there! I didn't realize it did actually have special handling.

> [@kornel](#):
>
> It wouldn't be a breaking change to relax only cases which currently lead to a compilation error.

I feel like I'm misunderstanding something else here, though. Wouldn't RFC 66 change the meaning of the following code?

```rust
struct NoisyDrop(u32);
impl Drop for NoisyDrop {
    fn drop(&mut self) {
        println!("dropped {}!", self.0);
    }
}
impl NoisyDrop {
    fn no_op_ref(&self) -> &NoisyDrop {
        self
    }
}

fn uses_noisy_drop(v: &NoisyDrop) {
    println!("used {}", v.0);
}

fn main() {
    let x = NoisyDrop(4).no_op_ref();
    println!("middle of function");
    // uses_noisy_drop(x)
}

```

Right now, this outputs

```rust
dropped 4!
middle of function

```

If the temporary `x` was extended to live through the function, as it would if it were `&NoisyDrop(4)`, I would expect it to change to instead output

```rust
middle of function
dropped 4!

```

[Next page](https://internals.rust-lang.org/t/request-for-a-smarter-lifetime-format-as-str-should-be-valid/12423.md?page=2)
