Returning temporary var

#1
#[derive(Debug, Clone, Copy)]
pub struct Vec2f(pub f32, pub f32);

impl Vec2f {
    pub fn new() -> Self {
        Vec2f(0., 0.)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct X<'a>{
    pub v: &'a Vec2f,
}

impl<'a> X<'a> {
    pub fn new() -> Self {
        X{
            //v: &Vec2f(0., 0.)  <-- This Works, why?
            v: &Vec2f::new()
        }
    }
}



fn main() {
    let x = X::new();
    println!("{:?}", &x);
}

(Playground)

1 Like

#2
//v: &Vec2f(0., 0.) <-- This Works, why?

It works because you’ve defined Vec2f() to be a tuple struct and you can define a tuple struct variable/static/constant/field in Rust like you do with structs but instead of the struct way:

struct Bar {
    eggs: i32
}

const Foo = Bar { eggs: 0 };

you do:

struct Bar(i32);

const Foo = Bar(0);

And yes, both ways are valid.

0 Likes

#3

It works because Vec2f(0., 0.) is a literal constant. So &Vec2f(0., 0.) is a reference to a constant, which has the 'static lifetime. The 'static lifetime is longer than any other lifetime, so you can always return it whereever a reference is required.

5 Likes

#4

In other words, this function here, (Which demonstrates the same behavior at compile time)

pub fn foo<'a>() -> &'a i32 {
    &0
}

can be annotated like so:

pub fn foo() -> &'static i32 {
    &0
}

because the data is constant, and is therefore embedded into the application. Similar to how this:

let x = "abc";

implicitly defines this:

let x: &'static str = "abc";

Therefore, in your example, because the memory representation of your tuple struct can be reinterpreted as a pair of f32s, they can be embedded into the end compilation result, and will be present for as long as your application will live.


Surely enough, if we do this in the playground, we can see that there is a static value there:

pub struct X<'a>{
    pub v: &'a Vec2f,
}

impl<'a> X<'a> {
    pub fn new() -> Self {
        X{
            v: &Vec2f(3.14159265358,
                      2.71828182846)
        }
    }
}

compiles into:

playground::X::new:
	pushq	%rax
	leaq	.Lanon.9113c71b4319b58548ae0c69a786d385.0(%rip), %rax
	movq	%rax, (%rsp)
	movq	(%rsp), %rax
	popq	%rcx
	retq

.Lanon.9113c71b4319b58548ae0c69a786d385.0:
	.ascii	"\333\017I@T\370-@"

(where we can see the static data in the .ascii entry at the bottom)

1 Like

#5

If you’re new to Rust, avoid using references in structs.

They don’t do what you think — they’re not for storing things by reference. In Rust the distinction is about ownership, which is different and takes some practice to “get”.

A temporary borrow & can’t exist without the thing it borrows having owned storage somewhere. The construct return &Something::new() doesn’t make sense in Rust, because it borrows a value that will cease to exist immediately after.

You can borrow value of a variable while this variable exists. You can borrow a field of an object while that object/field exists. You can borrow value of a literal/constant expression, because that expression is stored in your program’s executable. But you can’t borrow a temporary expression, because that value isn’t stored anywhere (yet).

4 Likes

#6

I will be surprised if your code did worked out. Others on this thread had pointed out and answered you but I think I should also point out an alternative way of achieving what you wanted…Another Playground…

Please check what i changed in your original post and some little comments. Hope it helps.

0 Likes

#7

Crystal clear, thanks

0 Likes