This is a purely syntactically sugar addition that would allow (in a most basic case)
let a = 5;
use b as a;
println!("{}", b); // 5
This addition would allow for 2 things: convenience and clarity. However also provides some practical benefits, take the following example:
let cmd_buffer = &mut renderer.gfx.command_buffers[renderer.gfx.frame_idx];
unsafe {
cmd_buffer.bind_graphics_pipeline(&self.pipeline);
self.vertex_buffer.bind(&mut renderer.gfx);
cmd_buffer.bind_graphics_descriptor_sets(
&self.pipeline_layout,
0,
std::iter::once(&renderer.gfx.desc_set),
&[],
);
cmd_buffer.draw(0 .. 3, 0 .. 1);
}
That does not work because renderer.gfx is borrowed multiple times. However this does:
// let cmd_buffer = &mut renderer.gfx.command_buffers[renderer.gfx.frame_idx];
unsafe {
renderer.gfx.command_buffers[renderer.gfx.frame_idx].bind_graphics_pipeline(&self.pipeline);
self.vertex_buffer.bind(&mut renderer.gfx);
renderer.gfx.command_buffers[renderer.gfx.frame_idx].bind_graphics_descriptor_sets(
&self.pipeline_layout,
0,
std::iter::once(&renderer.gfx.desc_set),
&[],
);
renderer.gfx.command_buffers[renderer.gfx.frame_idx].draw(0 .. 3, 0 .. 1);
}
In this case cmd_buffer is only being put into a variable because writing renderer.gfx.command_buffers[renderer.gfx.frame_idx] is annoying. Other languages have something similar to this, such as c++'s using statement. I think that rust could use something similar.
Why isn't this good enough?
unsafe {
let gfx = &mut renderer.gfx;
let frame_idx = gfx.frame_idx;
gfx.command_buffers[frame_idx].bind_graphics_pipeline(&self.pipeline);
self.vertex_buffer.bind(gfx);
gfx.command_buffers[frame_idx].bind_graphics_descriptor_sets(
&self.pipeline_layout,
0,
std::iter::once(&gfx.desc_set),
&[],
);
gfx.command_buffers[frame_idx].draw(0 .. 3, 0 .. 1);
}
Having multiple mutable paths to the same value is exactly the problem that Rust tries to prevent.
Unless I am misunderstanding (which I may be) this wouldn't be multiple paths to the same value because once compiled it would be identical. In that case it would suffice it was just the example that brought on the thought.
Preventing multiple paths isn't for the benefit of the compiler, it is for the benefit of the programmer. It makes code easier to understand and change. Trying to keep track of multiple mutable paths to the same value is hard (in general), and shouldn't be taken lightly.
Yep that makes sense. If I can ask why are use statements for crates/modules allowed aliases? If these also can store values.
If I can ask why are use statements for crates/modules allowed aliases? If these also can store values.
crates/modules are not names of runtime values, they do not "store values," so the motivation doesn't necessarily transfer to variables. It is true that variables within a module that can store values can also be renamed by use statements, but that doesn't affect the semantics or typical motivation behind this renaming.
AFAIK aliasing crate/module names is a common feature in many languages because the crate/module, or the full path to something inside it, often has a long descriptive name that would be annoying to type repeatedly, but is easily abbreviated without loss of clarity, e.g. use serde::ser::Serialize as Serialize. And in systems languages like Rust, it's also often useful to have a bunch of platform-specific crates/modules that you give the same alias so your #[cfg(...)] soup can stop at the use statements.
While the first part of that reasoning also applies to variables, you can already trivially reassign any variable to a variable with a shorter name, or use different names for arguments and parameters, so there's simply no reason to add yet another way to "name alias" a variable. Or more fundamentally: most crate/module names you write are chosen by others, but most variable names you write are already entirely under your control.
That does not work because renderer.gfx is borrowed multiple times. However this does:
...
In this case cmd_buffer is only being put into a variable because writing renderer.gfx.command_buffers[renderer.gfx.frame_idx] is annoying. Other languages have something similar to this, such as c++'s using statement. I think that rust could use something similar.
Lots of confusion here. First, if it literally worked as your example implied, this feature would be a way to turn off the borrow checker without actually fixing the reason the borrow checker didn't accept the code (@KrishnaSannasi already showed a proper fix), which is obviously a non-starter. Second, using in C++ does pretty much exactly what use ... as ... does in Rust, with no impact on runtime semantics; they can't change the fact that foo.bar has to do a field access somehow.
It's probably worth mentioning that a lot of these misunderstandings likely come from the fact that in traditional OOP languages like C++, a "class" is really a grabbag of several different features. What's relevant here is that declaring a C++ struct/class both creates a user-defined type and creates a sort of module/namespace. In Rust, structs and modules were always totally separate, and C++ has since recreated namespaces and modules as totally separate features.
That makes a lot of sense, thanks for the thorough explanation, should this be marked as closed now?
Most helpful comment
crates/modules are not names of runtime values, they do not "store values," so the motivation doesn't necessarily transfer to variables. It is true that variables within a module that can store values can also be renamed by
usestatements, but that doesn't affect the semantics or typical motivation behind this renaming.AFAIK aliasing crate/module names is a common feature in many languages because the crate/module, or the full path to something inside it, often has a long descriptive name that would be annoying to type repeatedly, but is easily abbreviated without loss of clarity, e.g.
use serde::ser::Serialize as Serialize. And in systems languages like Rust, it's also often useful to have a bunch of platform-specific crates/modules that you give the same alias so your#[cfg(...)]soup can stop at theusestatements.While the first part of that reasoning also applies to variables, you can already trivially reassign any variable to a variable with a shorter name, or use different names for arguments and parameters, so there's simply no reason to add yet another way to "name alias" a variable. Or more fundamentally: most crate/module names you write are chosen by others, but most variable names you write are already entirely under your control.
Lots of confusion here. First, if it literally worked as your example implied, this feature would be a way to turn off the borrow checker without actually fixing the reason the borrow checker didn't accept the code (@KrishnaSannasi already showed a proper fix), which is obviously a non-starter. Second,
usingin C++ does pretty much exactly whatuse ... as ...does in Rust, with no impact on runtime semantics; they can't change the fact thatfoo.barhas to do a field access somehow.It's probably worth mentioning that a lot of these misunderstandings likely come from the fact that in traditional OOP languages like C++, a "class" is really a grabbag of several different features. What's relevant here is that declaring a C++ struct/class both creates a user-defined type and creates a sort of module/namespace. In Rust, structs and modules were always totally separate, and C++ has since recreated namespaces and modules as totally separate features.