为变量创建别名。

huangapple go评论60阅读模式
英文:

Creating an alias for a variable

问题

I have the following code in Rust (which will not compile but illustrates what I am after). For readability purposes, I would like to refer the same string with two different names so that the name of variables passed to a function match their real meaning. This can be "solved" if I clone date_created but then I'm needlessly copying data.

let date_created: String = utc_now_str();
let paste_state_date = date_created;

// ...

some_function( date_created, paste_state_date );

Is there an efficient idiomatic way to create this "second name" for the same variable?

英文:

I have the following code in Rust (which will not compile but illustrates what I am after). For readability purposes, I would like to refer the same string with two different names so that the name of variables passed to a function match their real meaning. This can be "solved" if I clone date_created but then I'm needlessly copying data.

let date_created: String = utc_now_str();
let paste_state_date = date_created;

// ...

some_function( date_created, paste_state_date );

Is there an efficient idiomatic way to create this "second name" for the same variable?

答案1

得分: 7

In Rust, assignment works differently than in C++. When you assign a value to another variable, Rust may either move the value (in the case of non-copy types) or copy the value (for simple types like integers, floats, and shared references).

For example:

  • let x = "hello".to_string(); will move the value from x to y.
  • let x = 10; will copy the value into y.

Rust's ownership system ensures that you don't create dangling pointers, unlike C++.

Additionally, a String in Rust is a "fat" pointer, consisting of length, capacity, and a pointer to heap memory where the string is stored. When you borrow a String, it points to this stack representation, which could become invalid if the original String is moved elsewhere.

Rust automatically follows pointer chains to retrieve the actual string, which is why println!("{}", pref); outputs the string, not an address.

If you have more questions or need further clarification, feel free to ask.

英文:

> I thought I understood that, but the following does not work because
> constructing the reference to the original borrows the original and so
> it seems I can't use both during the invocation of a function. Am I
> missing something really simple?
>
> let str1 = ...;
> let str2 = &str1;
> my_func(str1, *str2);

Are you familiar with how assignment in Rust works differently than assignment in C++? Here's a simple example:

 let x = "hello".to_string();
 let y = x;

What do you expect the values of x and y to be after those statements execute in Rust? Answer: x has no value, y has the value returned by "hello".to_string(). That's known as "a move" in Rust--in other words, assignment moves values from one variable to another.

In C++, if you write:

string x = "hello";
string y = x;

then C++ will copy "hello" into y, which will leave you with two strings in memory. You have to be aware of that difference when using assignment in Rust.

What can be confusing is that sometimes when you use assignment in Rust, the value will be copied. For instance:

let x = 10;
let y = x;

After those statements execute in Rust, both x and y have the value 10. In this case, Rust copies the value into y. For simple values, like integer, float, byte, bool, and shared reference types, assignment copies the value into the new variable.

The reason the compiler won't let you write something like:

fn go(orig: String, orig_ref: &String) {
    println!("{orig}");
    println!("{orig_ref}");
}

fn main(){

    let pig = "pig".to_string();
    let pref = &pig;

    go(pig, pref);

}

is because calling the function involves an assignment:

 go(  pig     ,         pref);
       |                |
       V                V
fn go(orig: String, orig_ref: &String)  


    orig = pig      orig_ref = pref

Therefore, the String that was in pig is moved into orig, and pig becomes "uninitialized". That leaves pref pointing to pig, which is no longer valid. Rust was created to prevent C++ errors, like dangling pointers, so the compiler won't let you create a dangling pointer.

See the following for a more detailed description of how Python, C++, and Rust all take different approaches to the way assignment works:

https://stackoverflow.com/questions/76151512/how-to-reuse-a-struct-instance-with-rust

Because you seem to know C++, let's talk pointers. According to "Programming Rust (Revised 2nd edition)", a String type is a "fat" pointer: it consists of a length and a capacity, in addition to a pointer to some memory on the heap where the string is actually stored. For instance, if you write:

   let pig = "hi".to_string();

then this is the situation:

为变量创建别名。

The length is the actual length of the string and the capacity is how many total characters can fit in the allocated memory before Rust needs to allocate more memory for the string. Like a regular pointer, a "fat" pointer is efficient to copy because it is only three "machine words" long--no matter how much memory is allocated on the heap.

Then if you write:

   let pref = &pig;

I think this is what happens:
为变量创建别名。
pref doesn't point to the memory on the heap where the string is stored, rather pref points to the 3-word String on the stack. Subsequently, if the String in pig is moved elsewhere, i.e. the 3-word fat pointer is copied to some other location on the stack and Rust "uninitializes" pig, then pref would become a dangling pointer.

Rust will automatically follow chains of pointers to pointers in order to retrieve the actual string, and that is why when you write:

 println!("{}", pref);

Rust will output the string rather than an address.

huangapple
  • 本文由 发表于 2023年5月25日 00:53:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76325831.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定