There is a difference between a local variable from an argument?

Code with commence:

#[allow(unused_variables)]
#[allow(unused_assignments)]

fn main() {
        macro_rules! argment_assignment {
        ($name:ident, $expr1:expr, $type:ty, $expr2:expr) => {
            fn $name<'a>(mut arg: &'a $type) {
                let x = $expr1;
                let keeper = &*arg; 
                let mut px: &'_ $type = &x;
                {
                    let y = $expr2;
                    // local reference can be assigned to y
                    px = &y;
                    // String can't which is short than 'a
                    // str is loger than 'a, it can
                    arg = &y;
                }
                //  re-assign without deref is allowed
                px = &x;
                // not allowed for String
                arg = keeper;
                println!("{}", arg);
            }
            let mut x = $expr2;
            $name(&x);
        };
    }
    
    // this line can be compiled
    argment_assignment!(_ok, "1", str, "2");
    
    // but not this line
    //argment_assignment!(_failed, String::from("1"), String, String::from("2"));
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=104bdc43df7dbb67c7be5e5038dcf764

So, I surprised to know there is a difference between args and local variable, and I don't know why.

Your macro would expand to something like this. This is stripped down to the essentials.

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
    }
    px = &x;
    arg = &x;
}

The trouble is that the string is created in the method and assigned to x. Then you assign a reference to that string to arg which is a passed in reference. At the end of the function, x is dropped. However you are trying to keep a reference to it stored in arg after the function ends. For your str example, "1" for example has a 'static lifetime which it means it lasts for the lifetime of the program, so even at the end of the function call, it still lives on. This is effectively because any str's declared in your sources using "...." are compiled into the program so the reference to them is pointing to a piece of memory in the binary or library which will to last as long as the program is running.

For everything px related on the other hand that's all internal to the method, so it doesn't need to last longer than the method.

Oh, yes, my fault, problem is:

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
       // this line can compile
        px = &y;
        // this line can't be compile
        arg = &y;
    }
    px = &x;
    arg = a;
}

px is declared to have type &String, so the lifetime is deduced from &x. On the other hand, arg is explicitly specified to be &'a String, so its lifetime is determined by the argument specified by the caller, which is always larger than the function scope.

Yes, so the compiler knows &x is longer than '&y', and compiler let px = &x; to be compiled because no deref px to y happens after y died.

Same, arg = a; make compiler know no deref px to y happens.
But compiler does not realize this

You still have the same issue. The memory for x is allocated inside the function and it’s not returned as an owned value so the memory is destroyed at the end of the function but you are trying to return a reference to it.

1 Like

No, a = &*arg; not &x

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
       // arg = &y;
    }
    px = &x;
    arg = a;
}

this can be compiled

But if you have a reference to a value, another variable must own the value. The type of arg by your function signature is a reference. The only thing that exists at the return of the function call is arg which is a reference and therefore doesn’t own anything. In your example, what do you think is the owner of the memory that’s been allocated?

1 Like

That compiles because you are assign to arg which was the original argument to `arg' So something at the call site owns the memory coming in and that doesn’t change in return.

you need compare arg with px;

  1. both have a longer life time than y;
  2. both assigned by &y
  3. both re-assigned to something that is long enough after y dies
  4. BUT one of them can't be compiled

It doesn’t feel like your reading my responses. px and y are pretty irrelevant to your issues. The entire issue is assigning to arg a reference to data that will get destroyed at the end of the function which would invalidate the reference. In the cases that compile, you don’t do that. In cases that don’t compile, you are doing that.

1 Like

Are you sure it is destroyed at end of function instead of the nested block which y lives?

when execute px = &x, y has died, and px is a reference point to it ---- same with arg.

Both of them reference the died y at that point.

For the issue you're talking about, the compiler effectively just doesn't care, because it knows that you are no longer using the value of px. If you change it to the following and actually try to use the data referenced by px that has been destroyed at the end of the inner scope, the compiler will fail.

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
       // arg = &y;
    }
    println!("{}",px);
    px = &x;
    arg = a;
}

Because it deferences px, which is the died y, not because the compiler does not care for it will not used.
Try this, it is used latter and it compiles:

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
       // arg = &y;
    }
    px = &x;
    println!("{}", px);
    arg = a;
}

The key point is px after y dies is still a valid mut vairable.
It can'e be deferenced, but it can be assigned to something else.

At least, this is the natural feeling.
At the root, I believe it is because of rust is still too naive to publish a stable behavior

the code can't be compiled either which is obviously not right.
{:p} is getting the address inside the reference, it does not need deref.
It is complicated for the compiler to do all the things right.
But it indeed the responsibility of the compiler than any one else.

You want take all the safety in the safe code, you want to be god, then BE GOD

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
       // arg = &y;
    }
    println!("{:?}", px);
    px = &x;
    arg = a;
}

To repeat my last reply:

Again, please note that the type of arg is &'a String, where 'a is a lifetime specified by the caller that is larger than the entire function. The compiler cannot infer the type of arg to be anything else, because it is explicitly specified.

px, on the other hand, is declared with the incomplete type &String, so the compiler is free to infer an appropriate lifetime that allows the function to compile.

In other words, when you explicitly specify something, the compiler never "infers" it to be anything else.

1 Like

Read the book before you proceed:
https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html

Search for "Dangling References".

Not agree.
Whatever the compiler infer, &x has to have a longer lifetime than &y.
That makes px and arg to be at same position.
But the following code compiles for px.

fn compile_failed<'a>(mut arg: &'a String) {
    let x = String::from("1");
    let a = &*arg; 

    let mut px: &String = &x;
    {
        let y = String::from("2");
        px = &y;
       // arg = &y;
    }
    px = &x;
    arg = a;
}

So what?

The example in the book shows exactly the kind of error you're getting in your own code. If you want to be able to return a String, then you have to either overwrite the contents of a &mut String provided through function arguments or you have to return the owned value from the function as-is (fn my_fn([…]) -> String). Choose one.

Rust is clearly in the right about this error. If Rust allowed what you were trying to do, you'd have a dangling reference and that would cause undefined behavior. In C/C++, you'd have the exact same problem except that in C/C++ you wouldn't be getting an error and instead simply have undefined behavior. If any language could be called naive it would be C/C++, because they chose to defer all responsibility to the user by "inventing" undefined behavior, but not introducing any safety mechanisms. Practice has shown, this is a security disaster.

1 Like

I don't want either of them.
What I am doing just emulates an environment in which px and arg have same condition and act differently.