英文:
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 fromx
toy
.let x = 10;
will copy the value intoy
.
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论