In the following code example the compiler can work out that the if blocks aren't reachable, and yet it still gives me an error.
const A_MODE: bool = false; // I manually edit this to switch "modes"
fn main() {
let a: Vec<u32>;
if A_MODE {
a = vec![1,2,3];
}
if A_MODE {
println!("a: {:?}", a); // error: borrow of possibly uninitialized variable
}
}
Rust Playground
I thought that maybe the compiler was really trying to tell me that I need to initialize a at some point, but this compiles fine:
fn main() {
let a: Vec<u32>;
println!("Finished.");
}
Is the error just because the Rust compiler isn't smart enough yet, or does this behaviour have some purpose? Is there any simple workaround which results in a similar code structure?
I know that I could restructure the code to make it work, but for my purposes the above structure is the most straight-forward and intuitive. My current work-around is to comment and uncomment code blocks, which isn't fun. Thanks!
The compiler does not expand constant expressions in the phase for validating lifetime and ownership, so it is not "obvious" to the compiler.
If you really don't want to run that block, you might want to use #[cfg] (or the cfg-if crate if you like if syntax).
fn main() {
let a: Vec<u32>;
#[cfg(a-mode)] {
a = vec![1,2,3];
}
#[cfg(a-mode)] {
println!("a: {:?}", a); // error: borrow of possibly uninitialized variable
}
}
This way, it will compile both usages without branching at all if a-mode cfg is set, and will not compile either of them otherwise.
The compiler is aware that constant expression conditions never change, but that is handled at a later phase of the compilation for optimizations like removing branching.
Related
I'm trying to learn Rust, but have hit a wall, tying to do something I expected to be relatively straightforward. I'm trying to write a simple blink example for the ESP32c3 MCU. I got a basic example working, but started running into compilation errors when trying to expand/generalize the example.
My project consists of a cargo workspace with two crates - entrypoint and blink.
I was able to get the following basic version working without issues:
// entrypoint/src/main.rs
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::blink;
fn main() {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
println!("Hello, world!");
blink();
}
// blink/src/lib.rs
use std::thread;
use std::time::Duration;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;
pub fn blink() {
let peripherals = Peripherals::take().unwrap();
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).unwrap();
for _ in 0..20 {
led.set_high().unwrap();
thread::sleep(Duration::from_secs(1));
led.set_low().unwrap();
thread::sleep(Duration::from_secs(1));
}
}
Then I wanted to improve error handling (stop using unwrap() inside the blink crate) and make the blink() function reusable (the Peripherals::take() call panics, if it's executed more than once).
I came up with the following changes to improve error handling. This version also worked fine, I'm only including it to get feedback on how idiomatic my approach is / what would you do differently? I'm guessing it would be better practice to make a custom error type or is it acceptable/common place to return a string slice as an error even in production code?
pub fn blink(count: i32) -> Result<(), &'static str> {
let peripherals = Peripherals::take().ok_or("Failed to take peripherals")?;
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
Next, I attempted to make the blink() function reusable by separating the Peripherals::take() call from the rest of the blink() function, so it could be called only once at boot. I know I could make the call in my entrypoint and pass the peripherals as an argument to blink(), but I wanted to keep the blink crate responsible for making the Peripherals::take() call. This is where I started running into issues.
Attempt nr. 1: My first approach was trying to use a global Peripherals variable. I quickly found out that won't work unless I wrap the global variable with the thread_local macro or wrap operations on the global variable into an unsafe block which I wanted to avoid. I tried a number of things, but couldn't get my code to compile when using thread_local.
Both with and without RefCell (I found articles suggesting to use RefCell, but after trying it and reading the docs, I didn't see a good reason to use it for my use-case), thread_local seems to wrap my global variable into a LocalKey. I'm not sure how to use the LocalKey, besides the with() function - I'd like to avoid using with(), if possible, since I need to move my code into a closure, making it harder to read. I'm also not sure how to keep the for loop outside of the closure and only initialize the led variable from inside the closure - usually I'd move the variable declaration out of the closure, initialized to null, but null doesn't seem to be a concept which exists within Rust as far as I can tell.
thread_local! {
static PERIPHERALS: Option<Peripherals> = Peripherals::take();
}
pub fn blink(count: i32) -> Result<(), &'static str> {
PERIPHERALS.with(| p | {
let peripherals = match p {
Some(peripherals) => peripherals,
None => return Err("Failed to take peripherals")
};
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
})
}
The above code resulted in the following compiler error:
error[E0507]: cannot move out of `peripherals.pins.gpio8` which is behind a shared reference
--> blink/src/lib.rs:19:47
|
19 | let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
| ^^^^^^^^^^^^^^^^^^^^^^ move occurs because `peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error
The same error occurs, if I try to dereference peripherals variable first:
...
let mut led = gpio::PinDriver::output((*peripherals).pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
...
Attempt nr. 2: As my next approach, I tried to write a struct with a couple functions which would act as a class. Unfortunately I ran into the exact same compiler error.
// blink/src/lib.rs
use std::thread;
use std::time::Duration;
use anyhow::Result;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;
use esp_idf_sys::EspError;
pub struct Blink {
peripherals: Peripherals,
}
impl Blink {
pub fn new() -> Result<Blink, &'static str> {
match Peripherals::take() {
Some(peripherals) => Ok(Blink{ peripherals }),
None => return Err("Failed to take peripherals")
}
}
pub fn blink(&self, count: i32) -> Result<(), &'static str> {
let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
}
// entrypoint/src/main.rs
use std::thread;
use std::time::Duration;
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::Blink;
fn main() {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
println!("Hello, world!");
let blink = Blink::new()?;
loop {
blink.blink(2).unwrap();
thread::sleep(Duration::from_secs(5));
}
}
error[E0507]: cannot move out of `self.peripherals.pins.gpio8` which is behind a shared reference
--> blink/src/lib.rs:23:47
|
23 | let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because `self.peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error
I don't have a good enough understanding of how borrowing, references, and/or variable moving/copying works in Rust just yet to be able to solve this. It seems to be drastically different from other (more traditional) languages I'm familiar with (C, C++, Java, JS/TS, Python, Dart).
Once again, I'd also really appreciate any best practice recommendations/corrections, if you find anything out of the ordinary in my code above.
The basic gist of the error can be reproduced with a simpler example:
struct Foo {
bar: String
}
impl Foo {
fn baz(&self) {
let s = self.bar;
}
}
What's happening is:
self has type &Foo, because the parameter is declared with &self, which is a shorthand for self: &Self
self.bar has type String, because bar is declared to be a String
this leads to an issue, we're trying to *move s out of self, but we only have a reference to self, not owned access to self
You'll need to find a way to make that work without owned access. Disclaimer, I've not used this crate before (and the setup instructions are a bit crazy), but here's what I could piece together from the docs for PinDriver::output
PinDriver::output takes an impl Peripheral, i.e. any type that implements the trait Peripheral
looking at the docs for Peripheral, we can see a list of structs that implement it.
Gpio8 does implement it, but this is no good, since we'd run into the "cannot move out of shared reference" issue again
in most Rust code, you might want to .clone() the gpio pin, however, there's no clone method (because it represents a unique handle to a physical resource, unless you've got some board that can grow new pins :p)
however, right at the bottom, there's this impl:
impl<T: DerefMut> Peripheral for T
where
T::Target: Peripheral { ... }
i.e. any type which implements DerefMut<Target = T> implements Periperal, as long as T also implements Peripheral
this means you can use &mut Gpio8. This makes sense, since you're mutating the state of the light, so you need a mutable handle to it. If you change blink() to take a &mut self, you should be able to write PinDriver::output(&mut self.peripherals.pins.gpio8)
FWIW, embedded Rust often uses clever type system tricks to verify more behavior at compile time (either to save precious CPU cycles or for better reliability). If you're new to Rust, it can be quite confusing, since it's arguably more "advanced" than most CLI apps, for example
I Googled some segfault examples in Rust, but none of crash now. Is Rust able to prevent all segfaults now? Is there a simple demo that can cause a segfault?
If unsafe code is allowed, then:
fn main() {
unsafe { std::ptr::null_mut::<i32>().write(42) };
}
results in:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.37s
Running `target/debug/playground`
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11: 7 Segmentation fault timeout --signal=KILL ${timeout} "$#"
as seen on the playground.
Any situation that would trigger a segfault would require invoking undefined behavior at some point. The compiler is allowed to optimize out code or otherwise exploit the fact that undefined behavior should never occur, so it's very hard to guarantee that some code will segfault. The compiler is well within its rights to make the above program run without triggering a segfault.
As an example, the code above when compiled in release mode results in an "Illegal instruction" instead.
If unsafe code is not allowed, see How does Rust guarantee memory safety and prevent segfaults? for how Rust can guarantee it doesn't happen as long as its memory safety invariants aren't violated (which could only happen in unsafe code).
Don't use unsafe code if you can avoid it.
Strictly speaking, it is always possible to trick a program into thinking that it had a segmentation fault, since this is a signal sent by the OS:
use libc::kill;
use std::process;
fn main() {
unsafe {
// First SIGSEGV will be consumed by Rust runtime
// (see https://users.rust-lang.org/t/is-sigsegv-handled-by-rust-runtime/45680)...
kill(process::id() as i32, libc::SIGSEGV);
// ...but the second will crash the program, as expected
kill(process::id() as i32, libc::SIGSEGV);
}
}
Playground
This is not really an answer to your question, since that's not a "real" segmentation fault, but taking the question literally - Rust program can still end with a "segmentation fault" error, and here's a case which reliably triggers it.
If you're looking more generally for something that will dump core, and not specifically cause a segfault, there is another option which is to cause the compiler to emit an UD2 instruction or equivalent. There's a few things which can produce this:
An empty loop without any side effects is UB because of LLVM optimizations:
fn main() {
(|| loop {})()
}
Playground.
This no longer produces UB.
Trying to create the never (!) type.
#![feature(never_type)]
union Erroneous {
a: (),
b: !,
}
fn main() {
unsafe { Erroneous { a: () }.b }
}
Playground.
Or also trying to use (in this case match on) an enum with no variants:
#[derive(Clone, Copy)]
enum Uninhabited {}
union Erroneous {
a: (),
b: Uninhabited,
}
fn main() {
match unsafe { Erroneous { a: () }.b } {
// Nothing to match on.
}
}
Playground.
And last, you can cheat and just force it to produce a UD2 directly:
#![feature(asm)]
fn main() {
unsafe {
asm! {
"ud2"
}
};
}
Playground
Or using llvm_asm! instead of asm!
#![feature(llvm_asm)]
fn main() {
unsafe {
llvm_asm! {
"ud2"
}
};
}
Playground.
NULL pointer can cause segfault in both C and Rust.
union Foo<'a>{
a:i32,
s:&'a str,
}
fn main() {
let mut a = Foo{s:"fghgf"};
a.a = 0;
unsafe {
print!("{:?}", a.s);
}
}
when tried impl a double linked list in rust, i found below unexpected error
if let Some(link) = self.tail.take() {
let x = link.borrow_mut();
link.borrow_mut().next = Some(node.clone());
} else { ... }
here link is inferred to be Rc<RefCell<Node<..>>> and compiler says:
Cannot borrow immutable local variable link as mutable.
After tried, I guess when use std::borrow::BorrowMut, the error occurs.
// compiles
fn test1() {
let a = Rc::new(RefCell::new(1));
let b = RefCell::new(1);
b.borrow_mut();
a.borrow_mut();
}
// doesn't compile
fn test2() {
use std::borrow::BorrowMut; // inserted this import!
let a = Rc::new(RefCell::new(1));
let b = RefCell::new(1);
b.borrow_mut();
a.borrow_mut();
}
here test2() fails to be compiled. I wanna know why it works this way.
What you want to call is the method RefCell::borrow_mut().
However, a is a Rc and not a RefCell, so it has no borrow_mut method. This is where auto-dereferencing enters the picture. Because Rc<RefCell<T>> implements the Deref trait, it can be automatically dereferenced to a &RefCell<T> at method calls, and that's exactly what happens in the first test.
Now, if we import the BorrowMut trait, the test stops working. This is because the BorrowMut trait also has a method called borrow_mut, which is implemented for all types:
impl<T: ?Sized> BorrowMut<T> for T { ... }
This means that there is now a borrow_mut method available for Rc, so no auto-dereferencing occurs, and our code calls the wrong method.
The most comprehensive explanation of how auto-dereferencing works is in this SO answer.
I find using (1..4)
fn main() {
for v in (1..4) {
println!("{}", v);
}
}
and {1..4}
fn main() {
for v in {1..4} {
println!("{}", v);
}
}
gets the same result. Is there any different semantics between "(1..4)" and "{1..4}" iteration?
They produce the same iterators. You can even omit parentheses/braces:
fn main() {
for v in 1..4 {
println!("{}", v);
}
}
You can enclose an expression with () or {} in general. There is a difference however: {} creates a block and you can write statements (like let) in it. There is also a very subtle difference in how expressions are parsed. Edit: I found a blog article that describes another difference in how coercion and borrowck works.
Usually () is preferred if you don't need statements.
There's no real useful difference. Both parenthesis and braces count as a single expression and function to alter the precedence. I'm pretty sure they have slightly different parsing rules, but at that point I'd guess there's a cleaner way of writing the code.
Note that in your examples, the idiomatic way would be to use neither:
fn main() {
for v in 1..4 {
println!("{}", v);
}
}
When needed, I feel I've only ever seen parenthesis used, never braces:
fn main() {
println!("{}", (1..4).count());
}
There are rare cases where curly braces provide more power. Since they serve to start a new scope, you can use them to "manually" transfer ownership in some tricky locations. For the purposes of the simple iterator described, there won't be any visible difference.
In addition to the existing answers I was interested what the difference would be in the mid-level IR.
Even though the braces introduce a new block, in this case there is virtually no difference even in the (Nightly) MIR - the compiler immediately recognizes that the block serves no other purpose than returning a Range.
Say we have a variable which (after being used for its intended purpose) should never be accessed.
(doing so may be valid as far as Rust is concerned, but its contents is logically not valid anymore within the context of the application).
In Python for example, you can simply do:
del myvar;
Is there a way to disallow future access for variables declared in the body of a function or references passed as arguments?
Note that typically scope can be used for this, however that doesn't work for function arguments.
There is an ugly workaround that works even for Copy values: Shadowing.
enum Void {}
fn foo(x: i32) -> whatever {
println!("{}", x);
let x: Void;
// Now `x` refers to an uninitialized variable with which you
// couldn't do anything even if it was initialized
}
However, the error messages are horrendous, and the intent is far from clear. I strongly urge you to reconsider whether you need this ability this badly. For the record, I have never seen explicit del being used for this purpose in Python code. Nor do I recall such a thing in any of the other code I've read.
You can pass it to any function that consumes its argument (as long as you have its ownership in the scope you want to do it in). An example is drop, which does exactly (and only) that. Any attempts to use it afterwards would result in "use after move" errors.
Simple example with foo as the block where you want to delete the argument s:
fn foo(s: String) -> whatever {
println!("{}", s);
drop(s); // fn drop<T>(_x: T) { }
println!("{}", s); // error: use of moved value: `s` [E0382]
// do stuff
}
Note that this is only possible for types which do not implement Copy.
Edit: I think I found a solution for Copyable types; they can be Boxed, after which they can be dropped:
let x = Box::new(5);
println!("{}", x); // ok
drop(x);
println!("{}", x); // error: use of moved value