Probably, with a slightly tricky ownership issue leading to use after free (a well known source of exploits http://cwe.mitre.org/data/definitions/416.html) e.g. allocate to the heap, pass to a function which deallocates it (assuming that it has ownership) and use it after the call, e.g.
Which compiles without warnings using Clang (unless -Weverything, and even then the warnings are not related to use-after-free), works "correctly" in O0 and O1 (prints "3" twice) then breaks starting at O2 (prints "3" then "0"). (note: it always prints "3" twice with GCC 4.8, showing how fun these things are)
meanwhile the equivalent
fn main() {
let v = ~3;
destroyer(v);
println!("{}", *v)
}
fn destroyer(val: ~int) {
println!("{}", *val)
}
refuses to compile and explains why:
test.rs:4:20: 4:21 error: use of moved value: `v`
test.rs:4 println!("{}", *v)
^
note: in expansion of format_args!
<std-macros>:224:8: 224:50 note: expansion site
<std-macros>:223:4: 225:6 note: in expansion of format!
<std-macros>:241:45: 241:63 note: expansion site
<std-macros>:240:4: 242:5 note: in expansion of println!
test.rs:4:4: 5:1 note: expansion site
test.rs:3:14: 3:15 note: `v` moved here because it has type `~int`, which is non-copyable (perhaps you meant to use clone()?)
test.rs:3 destroyer(v);
^
error: aborting due to previous error
which can be fixed either by explicitly cloning the value, or by altering the sub-function to not consider it owns the pointer (or by removing the `println!` call in `main()`, thus transferring the ownership of the pointer to the sub-function safely, of course)
Analogous example in C++11, using unique_ptr (which Rust's owned pointer models):
#include <stdio.h>
#include <memory>
void destroyer(std::unique_ptr<int> x)
{
printf("%d\n", *x);
// x is deallocated here
}
int main()
{
auto x = std::make_unique<int>(3);
// destroyer(x); ERROR: ownership must be transferred explicitly
destroyer(move(x)); // Override with an explicit move
printf("%d\n", *x); // Guaranteed to segfault
return 0;
}
This is somewhat more helpful, but sadly the compiler cannot monitor the state of x at compile time, like Rust. The upside is that this bug is easy to catch since the invalid access is guaranteed to try to access a null pointer, and will crash instead of giving garbled results.
> The upside is that this bug is easy to catch since the invalid access is guaranteed to try to access a null pointer, and will crash instead of giving garbled results.
GCC and LLVM optimize based on the assumption that null pointers are never dereferenced, so this is actually undefined behavior, no? Anything can happen.
You're right that it's UB, good point. It's hard to think what a sane compiler would do in this case besides going through with the dereference, though.
A pointer dereference allows the compiler to assume that a pointer is non-NULL, so it can then perform "invalid" optimisations (like dead-code removal), this post contains an very small example: http://blog.llvm.org/2011/05/what-every-c-programmer-should-...
For all intents and purposes no. unique_ptr is nothing more than a pointer container that disables copying but allows moving. Therefore only one unique_ptr should be owning a pointer at any given time (unless, like everything in C++, you go around it).
The downside of picking subtler, more intricate examples is that you waste your reader's time trying to understand the subtleties of the example, which isn't teaching them anything about Rust.
Another option is to say something like, "For the sake of brevity, this is a very simple and arguably obvious violation of safety. In practice, there are many subtle and hard-to-diagnose sources of unsafety in C++, even when you use safer abstractions like shared_ptr." This allows you to avoid getting sidetracked and losing your reader, while heading off skepticism of readers with more knowledge about C++.
meanwhile the equivalent
refuses to compile and explains why: which can be fixed either by explicitly cloning the value, or by altering the sub-function to not consider it owns the pointer (or by removing the `println!` call in `main()`, thus transferring the ownership of the pointer to the sub-function safely, of course)