I was having a look at generators in Rust, and decided to give it a try. I implemented the most basic example I could come find:
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn main() {
let mut generator = || {
println!("2");
yield;
println!("4");
};
println!("1");
generator.resume();
println!("3");
generator.resume();
println!("5");
}
However, when I try to compile I get this error:
error[E0599]: no method named `resume` found for type `[generator#src/main.rs:6:25: 10:6 _]` in the current scope
--> src/main.rs:13:15
|
13 | generator.resume();
| ^^^^^^
Am I doing something wrong or the resume() method actually does not exist on generators? If so, how can I wake up a generator? The documentation I could find all points to the resume() method, so I am confused now.
Note: Yes, I am aware that this is an unstable feature of the language, but on the doc it does encourage users to try and use it, so I am assuming it should be at least partially implemented?
My current environment: rustc 1.35.0-nightly (acd8dd6a5 2019-04-05)
Yes, I am aware that this is an unstable feature
Unstable features tend to change over time and this is the case. With the recent stabilization of Pin the signature of resume has been changed (PR #55704)
fn resume(self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
But fortunately the examples has also been adopted.
So your code needs to look like this:
#![feature(generators, generator_trait)]
use std::ops::Generator;
use std::pin::Pin;
fn main() {
let mut generator = || {
println!("2");
yield;
println!("4");
};
let mut pin = Pin::new(&mut generator);
println!("1");
pin.as_mut().resume();
println!("3");
pin.as_mut().resume();
println!("5");
}
The upside of this is, that this approach does not need unsafe anymore (which your code is missing and therefore should not compile on an older nightly) and that's why the Pin API has been choosen for the generator.
Lessons learned: If you are using the nightly compiler, also use the nightly doc.
Related
I am porting an SDK from Java&Golang to Rust. These SDKs work auto-generating boilerplate code like this using templates (In java using OOP and in Golang using Reflection):
trait RPC {
fn decode(&self, frame: &Frame) -> ();
fn execute(&self) -> ();
}
struct MyRPC{
decoded: String
}
impl MyRPC for RPC{
//default implementations of decode and execute methods.
}
I would like that my user could do something like this so the only code that they need to write is the business logic:
include("autogeneratedCode")
impl MyRPC{
fn execute(&self){
//Do something with &self.decoded
}
}
execute() function should be called using an observer pattern:
let subscribers: HashMap<String, Box<dyn RPC>>
pub fn publish(&self, frame: &Frame) -> () {
for subscriber in self.subscribers.iter() {
let mut cloned_subscriber = subscriber.1.clone(); // <<1
cloned_subscriber.parse(frame); // <<2
cloned_subscriber.execute(); // <<3
}
}
I'm not able to make this code work in Rust having this problems:
variable does not need to be mutable Note: #[warn(unused_mut)]on by default Help: remove thismut. I guess the problem is that subscriber.1.clone() is a Box<dyn &RPC> and being a reference we should not mutate it?
cannot borrow **cloned_subscriberas mutable, as it is behind a&reference [E0596]cloned_subscriberis a& reference, so the data it refers to cannot be borrowed as mutable.
This is executing the default implementation and not the one in MyRPC written by the SDK user.
I feel that I am trying to force Rust to work object oriented, and maybe that is the problem. Should I use a more Rusty approach? I really like our current Golang/Java implementations using this "pattern" since the user only need to write the execute() function and all the underling machinery works, but maybe this won't be an option in Rust.
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
Was writing some code in Rust trying to define a CLI, using the (otherwise pretty great) crate clap, and run into a rather critical issue. Methods of App accept an Into<&'help str>, and i've been unable to find a way to implement this trait.
Indeed from what i understand, it is absolutely unimplementable:
struct JustWorkDamnIt {
string: String
}
impl From<JustWorkDamnIt> for &str {
fn from(arg: JustWorkDamnIt) -> Self {
return arg.string.as_str()
}
}
...which yields:
error[E0515]: cannot return value referencing local data `arg.string`
--> src/cmd/interactive.rs:25:16
|
25 | return arg.string.as_str()
| ----------^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `arg.string` is borrowed here
Interestingly enough, however, returning a literal compiles just fine (which i reckon is why clap doesn't mind using this trait). Presumably that's because the literal is compiled to some static region of memory and therefore isn't owned by the function:
impl From<JustWorkDamnIt> for &str {
fn from(arg: JustWorkDamnIt) -> Self {
return "literal"
}
}
But, i mean, surely there's a way to implement this trait and return dynamic strings? Maybe some clever use of Box<> or something, idk. I believe i've tried all the things i could think of.
And if there isn't a way, then this seems like a pretty glaring flaw for Rust - traits which can be declared and used in function headers, but cannot be actually implemented meaningfully (there's not much of a point in returning a literal). If this turns out to be the case i'll create an issue on the rust-lang repository for this flow, but first i wanted to sanity-check my findings here.
UPD: Thanks for the comments, made me think more in-depth about this issue.
The problem has to do with lifetimes, it seems. I apologize for not showing the entire code, i thought the snippets i shared would describe the problem sufficiently, but in hindsight it does make sense that the context would be important with Rust's lifetimes at play.
I did find a "solution" for this particular instance of the problem. Since the code in question will only run once per executable start, i can just Box::leak the &'static str and call it a day. Still, i would like to figure out if there's a more general solution which could be used for often-created dynamic strings.
impl Cmd for InteractiveCmd {
fn build_args_parser<'a, 'b>(&'b self, builder: App<'a>) -> App<'a> {
// leak() works here but i'm curious if there's a better way
let staticStr : &'static str = Box::leak(Box::from(format!("Interactive {} shell", APPNAME).as_str()));
let rv = builder
// pub fn about<S: Into<&'b str>>(mut self, about: S) -> Self
.about(staticStr)
.version("0.1");
return rv;
}
}
fn main() {
let interactive = InteractiveCmd::new();
let mut app = App::new(APPNAME)
.version(APPVER)
.author(AUTHORS)
.subcommand(interactive.build_args_parser(App::new("interactive")));
}
Currently i am faced with 2 points of confusion here:
Why is there no impl From<String> for &str? The compiler claims that one exists for impl From<String> for &mut str, and i'm not seeing the importance of mut here.
...and if there is a good reason to not impl From<String> for &str, would it make sense to somehow discourage the usage of Into<&str> in the public APIs of libraries?
Or maybe the issue is with my build_args_parser function and it's just not an option to offload the work to it as far as Rust is concerned?
Seems like you're trying to convert a String into a &'a str. You can do it like this:
use clap::App;
fn main() {
let s = format!("Interactive {} shell", "Testing");
let app = App::new("Testing")
.about(&*s); // or `.about(s.as_str());` it's the same
}
Here's the signature of the function that we want to call:
pub fn about<S: Into<&'b str>>(self, about: S) -> Self
So the about parameter must implement trait Into<&'b str>. According to std::convert::Into, we know that &'b str does implement trait Into<&'b str>. So now we just need a way to convert String into &'b str. The std::string::String tell us that there are two ways: use either s.as_str() or &*s.
I am currently writing a lint to find places where the use of std::borrow::Cow may be beneficial. Those places may include local variables, but also struct fields and enum arguments, unless they are part of the public interface (at which point I intend to bail; I don't want to ask people to change official interfaces at this point).
However, to do this, we have to check the whole crate for definitions before reporting. Since the LintPass trait has no callback that is called unconditionally after the crate has been walked, I am trying to implement rustc_front::visit::Visitor by my own visitor struct that encapsulates the rustc::lint::Context and our data.
Furthermore, I want to check fields of type String which are somewhere instantiated from a &'static str. To make this feasible, I'd like to use the ExprUseVisitor trait with my visitor struct.
The code as of now is here.
I get the following error:
src/cow.rs:56:44: 56:48 error: cannot infer an appropriate lifetime for lifetime parameter `'v` due to conflicting requirements
src/cow.rs:56 let vis = euv::ExprUseVisitor::new(self as &mut euv::Delegate<'t>, &infcx);
^~~~
src/cow.rs:51:5: 58:6 help: consider using an explicit lifetime parameter as shown: fn visit_fn(&mut self, _: FnKind, fd: &FnDecl, b: &Block, _: Span, id: NodeId)
src/cow.rs:51 fn visit_fn(&mut self, _: FnKind, fd: &FnDecl, b: &Block,
src/cow.rs:52 _: Span, id: NodeId) {
src/cow.rs:53 let tcx = &self.cx.tcx;
src/cow.rs:54 let param_env = Some(ty::ParameterEnvironment::for_item(tcx, id));
src/cow.rs:55 let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, param_env, false);
src/cow.rs:56 let vis = euv::ExprUseVisitor::new(self as &mut euv::Delegate<'t>, &infcx);
...
This error is quite surprising, to say the least. Note that the suggested "explicit lifetime" is actually the same as my code.
So how do I get my code to compile?
The problem is in your implementation of euv::Delegate. Specifically, you tried to implement euv::Delegate<'v> for CowVisitor<'v, 't> when you meant to implement euv::Delegate<'t> for CowVisitor<'v, 't>.
In general, if you're doing anything complicated with lifetimes, the error messages are nearly useless; don't trust that they're actually pointing at the error.
As stated in the documentation Vec has to be stable in 1.0-beta. However, if I try to call resize I see the following error message:
error: use of unstable library feature 'collections': matches collection reform specification; waiting for dust to settle
It can be easily reproduced, e.g. here: http://is.gd/IhEfEu
fn main() {
let mut v = vec![1, 2, 3, 4];
v.resize(10, 0);
}
A function can be marked as unstable, and Vec::resize is:
The yellow bar on the left indicates stability, and hovering over it describes why it is unstable.
I think it is an issue in the documentation.
The stable attribute can be applied to the crate, to the type or to individual functions, but in the documentation you cannot see the deprecation status of functions, only of types and crates.
If you go to the source, you will see:
impl<T: Clone> Vec<T> {
/// ...
#[unstable(feature = "collections",
reason = "matches collection reform specification; waiting for dust to settle")]
pub fn resize(&mut self, new_len: usize, value: T) {
And, as you can see in this file, there are still a lot of unstable functions in this module.