-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Explicit safe interfaces for unsafe code #193
Conversation
The problem: | ||
|
||
Rust's current `unsafe` blocks are a two-part construct: | ||
1) They allow the programmer to perform unsafe operations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(FWIW, these aren't being rendered as markdown lists, I think they need to be 1.
, 2.
(i.e. .
, not )
) and possibly need a newline before the first item.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it up, thanks.
I'd still like to see an RFC for a lint annotating the purpose of an Requiring unsafe blocks in unsafe functions is an interesting idea - but it will get a bit redundant when you are calling an unsafe function from an unsafe function (e.g. a |
@bharrisau As for requiring unsafe blocks in unsafe functions, this idea has floated around before and gotten some support. The main idea of it is to make it easier to identify which operations in the function are the unsafe ones. It also turns the |
@ecl3ctic Yes, but I imagine this would be implemented as an Your RFC is really covering three things - require unsafe blocks in unsafe functions, add unsafe fields to structs, and implement a lint and one/some annotations to help document unsafe parts. And the logic behind having unsafe blocks in unsafe functions is so you can say "the unsafety is here, but I can't contain it to just this function - some of it leaks out". |
@bharrisau I understand what you're saying, but I don't think a lint is a powerful enough solution. I believe it should be a standardized error, with no option for warn, so that all functions containing unsafe code are properly documented with one of two choices: "this function is unsafe, and I have qualified it as such", or "I claim this function has a safe interface, so I override the requirement for the unsafe qualifier". An unsafe block lying within a safe function without documentation should not be permitted under any circumstances. It doesn't tell you anything about where the safety boundaries are or how the function should be treated. A programmer can easily put one in and forget about it, leaving it unclear whether the function is really safe. Even if they're used correctly, the lack of clarity makes the code harder to maintain. |
-1: This is the problem with using 'unsafe' for a keyword in cases like this. The only thing 'unsafe' means is that it lies outside of the compiler's ability to verify it. Hence the programer has to state that they verified it independently to the compiler in order to compile it. Adding more extensive warning walls to a notion as simple as that is just useless boilerplate. |
@ecl3ctic Agreed - in terms of an intellectual point. In practice it will be much more flexible to have it as a lint and projects that want to be hard on safety can @ballard26 And for situations where the programmer can't state that it is verified in the scope of that one function? i.e. where they need to 'leak' some of the unsafety for other functions to provide the safety guarantee. |
First, a minor syntactic nit: I don't think Thinking out loud: There are a few things about the current design of
What this proposal is saying is that This seems to be broadly the right kind of idea to me, even if I'm not entirely sure about the particulars. Two specific observations:
|
I'm all for requiring unsafe { ... } blocks inside unsafe functions, as this narrows the scope of the unsafe actions and make them more visible when debugging (I also agree with @glaebhoerl that narrowing this down further to a statement level would be useful, although less visible). However, practically speaking I don't see any meaningful benefit of the #[safe_interface] attribute. "programmer assurance that a function is safe" is a meaningless statement to me. Obviously no one is going to deliberately write code that is unsafe and broken. By putting it into an unsafe block the programmer is acknowledging that they are stepping outside of the safety rules; practically speaking what does an additional attribute achieve? At best we could argue that it may encourage some people to use unsafe blocks less; at worst, it has no impact at all; people will simple add the attribute and hide the unsafe behavior just like before. If idiomatic code does not hide unsafe blocks in function, but rather declares the function unsafe, put it in the style guide and perhaps add a compiler lint like for camel case names. If we want to make it illegal to have hidden unsafe blocks in a function, make unsafe on function markers mandatory. This 'it's not ok, but it's actually ok if you like' achieves nothing. |
-1, this is just extra boilerplate. @bharrisau "where they need to 'leak' some of the unsafety for other functions to provide the safety guarantee" is what If you use an unsafe block, you are saying that its contents are safe in any context that function can be called in. If that's not the case, don't use an unsafe block- use an unsafe function. |
👎 This solves nothing. An Also, this is trying to sneak |
I think this RFC is a bit misguided. |
It could be, but since its effect is to have no effect (it obviates
I was considering this at first, but I decided against it for two reasons:
The point is to make the declaration of safety or unsafety explicit. Currently when you put an unsafe block in a safe function the rule is "assume the function is still safe" by default. Under the proposed new rules, it becomes "error by default; have the programmer choose explicitly between safe and unsafe". This will catch people who accidentally (or through lack of understanding) simply drop an
Correct code declares the function unsafe (unless they proclaim a safe interface). This has nothing to do with idiomatic Rust, it's about valid Rust. That's why the proposal forces the programmer to make the choice.
Ideally yes, except nobody seems to be doing this, as evidenced by numerous people claiming
I'm not sneaking anything in. I did acknowledge that it seems tangential to the rest of the proposal, but it's an important part of the "overhaul" I'm proposing, and Rust's |
@ecl3ctic
There are plenty of examples of FFI code (eg. malloc) which are accurately safe because they return Option. Are you suggesting these should be tagged unsafe? The weakness of the decorator is that there are cases where it's valid to have an unsafe block in a function that is safe. The decorator is a work around for these, but by existing it explicitly shows that the 'general rule' that 'functions with unsafe blocks should be unsafe' isn't true. It should be a lint or a compile error. A compile error that you can suppress with a decorator doesn't make any sense. |
No, absolutely not! If a function has a safe interface like that, that's when you override the requirement for an unsafe tag with I don't completely understand the rest of what you're saying. But to address
Functions with unsafe blocks should be whatever they need to be. The proposal is to assume they're nothing until the programmer says they're something. Currently we assume they're safe. |
@ecl3ctic perhaps I've misunderstood. From the RFC it seemed to be that:
Would be a compile error?
However, by adding the #[safe_interface] decorator, you allow this to compile.
So the identical code; one with #[safe_interface] compiles, one without #[safe_interface] does not compile. ie. You are suppressing a compile error with an attribute. Isn't that like creating an #[ignore_lifetimes] attribute that lets you mark a function with invalid lifetimes as valid, so the compiler will compile it no questions asked? I can't think of any other examples in rust where you can suppress compile errors in this way off the top of my head? |
What other things are they claiming it means?
As near as I can tell, the only motivation you have for making this change is to try and force programmers to double-check that they're not doing something wrong when they use The only change that I feel like would actually make a difference is to change the This solution is better than
|
Yes,
defaults to a compile error because the programmer hasn't specified whether the function interface is safe or unsafe. Annotating the function with I also suggested |
You can read comments around any discussion of
Yes, I agree, or the scope of the enclosing function at least, since nobody is going to wrap a whole 100 line function in
It's easy today to write some unsafe code to get the results you want, then forget about it because now your program "just works", or at least appears to. Assume the enclosing function is part of some large complex struct, and you go back to the code a month later to make changes. You have same unsafe code sitting there, but the implications of it are unclear. Is that function really going to be safe to call in all the new code you are writing, or was it just safe for what you were doing before? It wasn't documented whether the function exposed a safe interface, because it "just worked" back then and we didn't care beyond that. At no point did we make the choice between safe and unsafe, and the compiler didn't force us or ask us to label the function unsafe because of its contents. The proposal forces the programmer to do a little more thinking beyond "this works", and leaves it documented whether that function is really safe (by the programmer's word) or actually unsafe. There's no default. Yes the world isn't perfect and there will obviously be times where someone puts anything down to shut the compiler up or just gets it wrong, but that's unavoidable with any solution, and this solution is vastly better than what we have today. |
No, it's the only sane use of unsafe code. What you're describing is a cultural problem, not a technical one- people who will slap |
Boilerplate teaches people that the compiler is there to get in their way rather than to help them. The |
No it doesn't, and that's the problem. You put for this RFC because you are asserting this to be true, but there is no reason to believe that requiring the If people want to write code unsafely, technical countermeasures will not help. As @rpjohnst stated, this is a cultural problem, and the only solution is to fix documentation to stress the importance of using |
Thanks for taking the time to write up your thoughts into an RFC! We discussed this at today's triage meeting, and the conclusion we reached is that the complexity additions to the language don't necessarily outweigh the potential benefits of a new system of treading unsafe code. Many comments have also pointed out that this isn't necessarily a silver bullet to help writing unsafe code. With this in mind, we've decided to close this for now. |
Since having written this, I do admit that it isn't a perfect solution (and a better solution may not even exist). Perhaps a big effort towards teaching how to write unsafe code properly will ameliorate most of my concerns. |
Move oneshot away from `Slot`
This RFC proposes a reworking of Rust's
unsafe
features to force the programmer to declare (and think about) safety boundaries when they write unsafe code. The goal is to reduce mistakes when working with unsafe code that could ultimately affect program stability and security.View the formatted version here:
https://github.com/ecl3ctic/rfcs/blob/master/active/0000-explicit-safe-interfaces.md