Example about: i32 ref and i32 owned in fns

// E1:
//..
pub fn _new(v  :&i32)->Self{
    let ptr: *const i32 = v; // ref argument
    Self(v,ptr)
}
//...
//E2:
///..
pub fn _new(v :i32)->Self{
    let ptr: *const i32 = &v;  // owned argument
    Self(v,ptr)
}
//...

E1:Compiler Explorer
E2:Compiler Explorer

Question: In fn (self),Is

fn(self)->*const Self{ &self as *const i32} == fn(&self)->*const Self{ self as *const i32} 

?

E2 is UB because the pointer is only valid for the stack frame where v lives.

1 Like

That's right. :grinning_face_with_smiling_eyes: . It's modify from E1 so I have forgot remove a comment.

They are very different.

self is a local variable that always becomes invalid by the end of the function. In the first function, the *const i32 is pointing at that local variable, and so it is pointing at garbage after the function completes. It's a dangling pointer, and it is always invalid to dereference it at the call site.

In the second function, self is a reference to something that exists outside of the function. And the *const i32 is pointing at that same thing which is outside of the function. You never create a reference or pointer to the self variable in this case, you just effectively return a copy of one that already exists. How long the returned pointer is valid to dereference depends on what is going on at the call site.


struct Length(i32,*const i32);

pub fn from_ptr(&self) -> i32{
    // SAFE: Length drop then i32 drop too!!
    unsafe{self.1.read()}
}

When you move the Length around, the address where the i32 is changes, but the value of the *const i32 does not change, so this doesn't work like you seem to think it does.

2 Likes

There's a problem with the unsafe block in E1. The lifetime of v: &i32 in _new is unrestricted, so its address may or may not be valid at the time of from_ptr.

Here's an exploit of the unsafety. You can run it with Miri by selecting it in the "TOOLS" dropdown.

Apart from changing the code to use a reference, I can think of 2 ways to fix this: (I consider myself a beginner, feel free to correct me!)

1 Like

By the way, if you meant to create a self-referencing struct (i.e. the pointer points to an address inside Length, which I guess you want to be its i32 field) then I read that it requires using Pin to prevent the Length from being moved. I'm not really familiar with that, but I'll drop a link to the docs

And your current code is not self-referencing, E1's pointer points to wherever v points to (can be in main's stack frame, on the heap via &*b where b is a Box<i32>, etc), and E2's pointer points within _new's stack frame allocation.

1 Like

you are right!! I have through v in fn _new(v:&i32) doesn't move but it was wrong. ( and move 0x8)

thank for corrected me.