-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Unsafe expressions #1346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unsafe expressions #1346
Conversation
While it is usually used for single calls, I think that’s unidiomatic use of Usually you want to insert calculations that, when wrong, could cause unsafe {
let bytes = 1024;
do_something_with_str(somestr.slice_unchecked(0, bytes));
}
// rather than
let bytes = 1024;
do_something_with_str(unsafe {somestr.slice_unchecked(0, bytes) }); |
@nagisa that seems like arbitrary personal preference. As in, as long as it doesn't unreasonably tangle my code, i try to restrict the Reason: I don't want to accidentally (as in, without proper thought) insert an additional unsafe usage that the compiler ignores since I'm already inside an unsafe block. |
While I don't necessarily disagree, I dislike doing that because it feels like there's a danger of accidentally slipping in something unsafe when it wasn't intended. I see fn index_mut(&mut self, index: usize) -> &mut T {
assert!(index < self.len());
unsafe { self.get_unchecked_mut(index) }
} The assertion, which is very dangerous if wrong, is not included in the
This is why I take slight issue with unsafe functions, which make the entire function body implicitly |
I will say @nagisa 's style is the one that I adhere to as well. |
Yeah putting everything in a big That said, I'm a fan of "the big unsafe" because as soon as I've established "we're doing some unsafe stuff", I don't really care about what's individually the unsafe things. It seems kinda weird to me to be worried about someone coming along and adding a new unsafe thing unwittingly? |
I use the petite unsafe style myself for the same reason as @arcnmx. And sometimes the invariants that make the unsafe call okay are necessarily distant from the call (e.g. it could be inside a method on a struct). I think this is a personal style thing and not an idiomatic thing. Most of my unsafe code is ffi calls. I would enjoy being able to prepend unsafe to each ffi call rather than using blocks. |
+1 petite unsafe, to the point where I wish we had "safe blocks" to limit unsafety, especially in unsafe functions. |
This is something I've wanted for FFI-heavy code since I wrote my first line of FFI code in Rust. Yeah, yeah, I know; it's an external function, it could spray random image macros all over my address space. But it's Which then means breaking all the arguments out and binding them to variables outside the call and now the code, which was long to begin with (because that's what happens when you try to do anything with the Windows API in Rust) is now several times longer and I'm seriously starting to consider just writing this in C++ because I'm pretty much throwing away all safety anyway at this point. A slightly exaggerated example, but you get the idea. That said, I'm not hugely in favour of this; the above problem can be addressed by just writing wrappers around every single FFI function. Then again, I wouldn't complain if this was added. |
It also makes reasoning about unsafe infectiousness somewhat straightforward: "unsafe language items are infectious, unsafe expressions are not," expressions and language items being the two conceptual levels of Rust's syntax. |
I do not understand this RFC. In particular, I do not understand the relationship between the precedence of the dot-operator ( It seems like it is saying that But at the same time, it claims that This does not match my usual understanding of how an infix operator like |
not in favour - the gains here seem very minor - we only save typing |
I'm kind of indifferent here. I mean, from my POV, this is a small convenience, which is nice -- but it's a small one nonetheless. However I will note that others on the @rust-lang/lang team are less positive. I'll let them speak for themselves, but I think it's primarily a matter of not wanting to introduce more choices where they are not needed. (Oh, they already did while I was writing this. Oh well.) However, we have to figure out the precedence! I initially assumed that this had a very low precedence, like closures. Basically it would extend as far right as possible. But that doesn't seem to be what the RFC says, and I think it's going to be very hard to encode that into the grammar. |
@pnkfelix To clarify...
What's meant is that it means providing |
@arcnmx One might argue the absolute minimum would be The above admittedly looks absurd, since an identifier lookup is never unsafe, but my point is that in terms of how language grammars are typically defined, it is strange to have an infix operator (the " (Maybe my concern will be clearer if you consider an example like |
Or maybe this will be the simplest way for me to state my concern: You say:
but parsing expressions that lack delimiters (delimiters being the |
@pnkfelix Sorry, I'm clearly very terrible at expressing this. By absolute minimum, I meant "minimum that you could write with {} syntax and only wrap the relevant unsafe code". It's unrelated to how the expression syntax works, I'm simply stating that expression syntax provides less control over which part of the expression is covered as unsafe, because it would have such a low precedence. And therefore as a drawback means it could encourage more code wrapped in "unsafety" than may otherwise be expressed with the {} syntax. I'll update the wording and examples when I get a chance. I'm not aware of a full operator precedence table, so it's tough to accurately place it, but I'd place it somewhere lower than infix operators but higher than most binary operators. Consider these identical...
...and so on. @nikomatsakis mentioned it having closure operator precedence, which would actually make the last example EDIT: hm, actually... That also enables |
Perhaps I have been the victim of an invalid inference. You wrote:
but this appears to contradict the RFC text? Namely, the primary example of the RFC text says:
can now be rewritten as:
and the Drawbacks section strongly implies that the above is not equivalent to:
But then the very first example in your last comment implies that
is desugared to Am I misunderstanding something here? |
@pnkfelix this is what I meant by expressing the idea poorly :) I didn't mean to imply that the primary examples were identical in that the latter desugared into the first. I now realise that's confusing and will change it! The drawbacks section meant to imply that it was equivalent to the greedy unsafe (that greediness itself being the drawback!) |
I remember some similar confusion about the precedence of the |
The point of my comment, which upon re-reading I see that I didn't state explicitly, I think was that it might make sense to tie the precedence of |
Aha, I forgot we had a precedent set (hah) already with the My comment on |
As someone who uses unsafe quite heavily, I don't feel that being able to leave out {} is worth the potential ambiguity and confusion. After all, we require that other things like if and while must always have braces, so what is so special about unsafe that braces can be optional? |
@retep998 well, requiring braces is so stuff like if foo bar();
baz(); doesn't happen. (also presumably parsing issues with no parens around the condition?) But unsafe unsafe_op1();
unsafe_op2(); just doesn't compile. There's no way to misuse this construct, as far as I can tell. |
Updated RFC text for clarification on the operator precedence. |
It's a personal preference thing, really. I dislike braces because they seem like strange noise that normally should only indicate control flow or inner scope. I see unsafe as "warning this function call does scary things especially if its parameters are wrong take note" and not "this whole block of code is scary things". Context around unsafe is important, and you'll never encompass it all in a single block. The rightward indent drift is annoying to me when you don't necessarily intend to create a new scope. I also consider "this function is unsafe to call" and "this code does unsafe things" as two separate ideas, which is why I disagree with the idea that the body of an unsafe functions should be implicitly But offtopic, it's mostly about ambiguity. Any situation where an expression is followed by another expression, such as control flow statements that take a condition, must have some sort of unambiguous parsing of the two. That typically means either With |
Hear ye, hear ye. This RFC is entering final comment period. As of now, the general feeling was that we would most likely not accept the RFC. It is certainly true that |
I don't feel optimistic about this RFC being accepted, but I would still like it. I see this as useful mainly for calling a single unsafe function in the midst of otherwise safe code. Obviously, since its a stylistic thing, its not an awful loss either. Precedence ambiguity doesn't seem like too much of a problem, for the same reason the bracketless if scenario is not much of a problem - if precedence surprisingly puts an unsafe operation outside of the unsafe scope, the compiler will inform the user. This is also a fairly uncommon occurrence since most other operations in the precedence list are not unsafe. |
Mm, I'd like it, I think it's an ergonomic improvement over the current clunky required blocks, which just plain seem unnecessary, but it just doesn't seem like that's enough of a motivation to incite change! I don't like to think that the precedence is the main reason to not go through with it though... It's a talking point for sure, but it's not our first keyword prefix operator and it won't be particularly confusing whichever way is chosen. Either it will encompass a small part of a line (and result in compile error when misused), or it will encompass a large part of it but never beyond that one line/statement. A lot of the debate in this thread regarding the precedence was due to misunderstandings and poor wording in the first draft of the RFC. After that, |
FWIW, I'm not strongly opposed to this RFC, but like many others feel its downsides (extra syntactic complexity/axes of choice, general dislike for this style of prefix operators) outweigh its upsides (occasional slight improvement to ergonomics). I've not felt the ergonomic hit here much personally, despite having written a decent amount of code that could use this proposal. And, on the other hand, I somewhat appreciate the visual attention that the braces draw to today's |
As we forgot to issue the "final-comment-period" label, we're going to extend FCP by 1 week here. |
👎 to this. I don't see the utility in making Anyway, if there's more demand for this in the future then we can always reconsider later. |
👎. |
If this gets accepted, I'll personally add a lint against it to clippy. It'd probably be |
The lang team met to discuss this RFC and has decided to close. The rationale has already been laid out at various points on the thread, but to summarize:
Thanks, @arcnmx for the RFC! |
Yes the example is terrible, Idunno I just grep'd for
unsafe
and used the first thing I found ._.tl;dr
instead of
Rendered