2

I've discovered some behavior I cannot explain. I've written this function:

fn main() {
    let word_one = "ONE";
    let x;
    {
        let word_two = "THREE";
        x = get_ref(&word_one, &word_two);
    }
    println!("{}", x);
}

fn get_ref<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.chars().count() >= b.chars().count() {
        return a;
    }
    return b;
}

This function is possible, even if the lifetime of word_two isn't long enough to build a reference. Otherwise this example isn't possible:

fn main() {
    let word_one = "ONE";
    let x;
    {
        let word_two = "THREE";
        x = &word_two;
    }
    println!("{}", x);
}

Unfortunately I've discovered nothing in the docs which would explain this behavior.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
fennestral
  • 21
  • 3
  • [How does the lifetime work on constant strings / string literals?](https://stackoverflow.com/q/31230585/155423); [What are Rust's exact auto-dereferencing rules?](https://stackoverflow.com/q/28519997/155423) – Shepmaster Jul 19 '21 at 19:22
  • 2
    Note that `&word_one` is of type `&&str` and not `&str`, which is required by `get_ref`. So, there's some kind of coercion on call site - probably from `&'a &'static str` to `&'static str`. – Cerberus Jul 19 '21 at 19:24
  • 2
    TL;DR — passing `&&'static str` to a function expecting `&'a str` will automatically dereference once and then '`a` will be assigned with `'static`. – Shepmaster Jul 19 '21 at 19:24
  • And [here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e5fedf3e4ade2671afe3c08283494392) is the "fixed" version of `get_ref` that (without changing `main()`) fails to compile in the way you expect. I've made the method calls in `get_ref()` explicit to avoid confusion, but `a.chars()` would have also compiled because Rust auto-dereferences borrows when resolving method calls. – user4815162342 Jul 19 '21 at 19:46

1 Answers1

3

Rust's auto-derefencing rules are messing you up. What you've written is syntactic sugar for the following:

fn main() {
    let word_one = "ONE";
    let x;
    {
        let word_two = "THREE";
        x = get_ref(word_one, word_two);
    }
    println!("{}", x);
}

fn get_ref<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.chars().count() >= b.chars().count() {
        return a;
    }
    return b;
}

This is because word_one and word_two are of type & 'static str. They are references to underlying string objects which have been statically allocated.

On the other hand,

let x;
{
   let word = "word";
   x = &word;
}

does not work because there is no dereferencing magic. &word is of type &'a &'static str where 'a is the lifetime bounded by the braces. So the value does not live long enough.

Mark Saving
  • 1,752
  • 7
  • 11