Is it a good idea if the keyword mut is symbolized as ~
let mut a = 3;
let ~a = 3;
fn foo(a: &mut Vec);
fn foo(a: &~ Vec);
Is it a good idea if the keyword mut is symbolized as ~
let mut a = 3;
let ~a = 3;
fn foo(a: &mut Vec);
fn foo(a: &~ Vec);
No, Rust already uses a lot of symbols for syntax, adding more won't help. For example, this is a valid expression |()|()
. It's good to have keywords because it makes the code significantly easier to read.
You could write a macro to do this. There's no way this will change in the core language, though.
Rust used to have many more symbols, in the distant past. IIRC ~T
was how one used to spell Box<T>
, for example.
But Rust has consciously moved to have fewer sigils, so generally the answer to your question is no -- without substantial motivatation (like existed for ?
), the default assumption is that replacing letters with a symbol is not good.
EDIT: You can also see this in the await
conversation -- there were many suggestions to use a sigil for that, but the language team decided early on that the syntax would use those letters and not a new sigil well before the final form was decided.
No. I fail to see how replacing a keyword with a cryptic symbol helps anything much. Personally the less my code looks like a secure password or line noise resulting from communication errors the easier it is for me to read it. I rate readability as a good thing and more highly than conciseness.
I think not at all. It's less clear – Rust already has many symbols, and changing a keyword to a symbol would be detrimental to readability.
let mut
and &mut
are actually two different features of the language, with different guarantees. let x = 1; let mut x = x
is legal, but let x = &1; let x: &mut _ = x;
is not.
If anything was to be changed here, I'd suggest giving different keywords/syntaxes to these two features. But Rust is past 1.0, so we're stuck with the old syntax forever.
&mutx
→ mutually ecxclusive&shrd
→ shared&lazy
→ lazily initialized[1])would've been my preferred reference identifiers. "mutable" and "immutable" are kinda misleading when dealing with UnsafeCell
.
[1]: I hope we'll get a safe alternative to MaybeUninit
for simpler cases, eventually
What is &lazy
supposed to be?
fn set_u8(n: &lazy u8) {
// Compile error, n is write-only:
// println!("{}", n);
// Compile error, n must be initialized:
// return;
// <&lazy T>::set is compiler "magic"
let n: &mut u8 = n.set(123);
// works:
println!("{}", n);
return;
}
let a: u8;
// Compile error, a is uninitialized:
// println!("{}", a);
// one way to initialize a:
// a = 123
set_u8(&lazy a);
// works:
println!("{}", a);
I think this gets into territory that is incredibly difficult for the compiler to prove is safe. You're basically saying you should be able to take a borrow on an uninitialized value, pass it down some arbitrary call chain, and have the compiler prove that it's initialized somewhere before its first use.
EDIT: NVM, this is probably doable. But you should still just use something like like n: &mut Option<T>
.
Why is this difficult? Isn't this the same thing as out-references in C#?
Hm... maybe I over-estimated the difficulty.
My thought was that panics, and potentially catching those panics, could make it difficult, but I suppose that would result in a break in control flow, so there would be no issue. I just discovered that you can't exclusively borrow a value in a catch_unwind
block at all!
Oh, you sure can:
use std::panic::AssertUnwindSafe;
use std::panic::catch_unwind;
fn main() {
let mut x = 42;
let x_ref = AssertUnwindSafe(&mut x);
catch_unwind(move || {
*x_ref.0 += 1;
});
}
I worry about my future in Rust sometimes.
When I follow the link above I find:
Wrapper expressing the semantics of
&out T
referencesIn other words, this has the semantics of
&'out mut MaybeUninit<T>
but for the ability to write garbage (MaybeUninit::uninit()
) into it (else coercing&mut T
to&out T = Out<T>
would be unsound).This means that the reference may point to uninitialized memory (or not), and thus that writes to the pointee will not call the
.drop()
destructor.
I cannot begin to understand what that means or even why I would need one.
An important trick here is that out
doesn't actually exist in CIL -- to the runtime it's just a ref
. So there isn't actually a reliable validation that it was initialized, but that's ok because in C# (and other .net languages) everything has a default (and must, for things like arrays). So out
is actually just set-to-Default::default()
-and-pass-&mut
, or set-to-None
-and-pass-&mut
if you prefer to think of null
that way. Which of course also works fine in Rust.
And I think that if we did add something like C#'s checks, it would be awkward in that it would probably need to be set even in Err
paths. In a way we have "out parameters" once we get to MIR -- the _0
is passed as the location for the return value, which is expected to get initialized. So perhaps -> Option<T>
is really just Rust's preferred encoding of C#'s bool(out T)
.
Ah, I see. I thought C# had a hard requirement that you initialize the variable before leaving the function.