stm32bluepill-mynewt-sensor icon indicating copy to clipboard operation
stm32bluepill-mynewt-sensor copied to clipboard

Proper error handling

Open hellow554 opened this issue 5 years ago • 8 comments

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/send_coap.rs#L19-L23

Immediatly as I saw the video in the medium post I shuddered.

Do proper error handling in Rust!

Don't return ints to indicate errors or failures, use Result<(), ErrorType> instead. This is just one place, but everywhere where you have those ghastly assert!(rc == 0) things, throw them away and burn them! Either use unwrap, or better expect.

hellow554 avatar Jun 11 '19 11:06 hellow554

More examples:

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/send_coap.rs#L34-L37

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/listen_sensor.rs#L30

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/listen_sensor.rs#L70

(why is that one declared as extern although it's not pub?)

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/send_coap.rs#L20

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/send_coap.rs#L34

and finally you can convert the integer error codes to an enum

https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/f51e0784957723fcea8d3efaec10cbaacd1d8048/src/sensor.rs#L123-L139

hellow554 avatar Jun 11 '19 11:06 hellow554

Hi Marcel: Thanks for the tip! The porting of the entire application from C to Rust is still in progress, once I'm done with the meaty parts (e.g. CoAP messaging), I'll come back to the return types. And write an article about this too!

Here's different pattern for returning results: https://rust-embedded.github.io/book/static-guarantees/design-contracts.html

impl<IN_MODE> GpioConfig<Enabled, Input, IN_MODE> {
    pub fn into_input_pull_down(self) -> GpioConfig<Enabled, Input, PulledLow> {
        ....

//  Pull down the GPIO pin
let pulled_low = input_pin.into_input_pull_down();

Any update operation on GpioConfig returns another GpioConfig object. It's like a chainable API.

Wonder if you have any thoughts on this approach?

lupyuen avatar Jun 11 '19 12:06 lupyuen

Also what do you think about error-chain?

http://brson.github.io/2016/11/30/starting-with-error-chain

lupyuen avatar Jun 11 '19 12:06 lupyuen

Here's different pattern for returning results

No, that's not returning a Result (as you can see by the return type). It's just returning a struct.

Also what do you think about error-chain?

http://brson.github.io/2016/11/30/starting-with-error-chain

IIRC error-chain is no longer recommended for new designs. You can of course roll your own Error enum, which should be no problem at all. You don't need a framework. You don't get any real benefits from it when working on an embedded platform.

hellow554 avatar Jun 11 '19 13:06 hellow554

Thanks Marcel, I'm testing expect() like this...

pub fn start_network_task() -> Result<(), i32>  {  //  Returns an error code upon error.
    Ok(())
}

    //  In main()
    let rc = start_network_task();  
    rc.expect("");

When I did this, the compiled ROM size bloated from 55 KB to 58 KB. I suspect this could be due to core::fmt, I need to check.

Any idea how I can cut this bloat?

lupyuen avatar Jun 11 '19 13:06 lupyuen

I hope you are compiling with --release.

expect("") should be expect("start_network_task failed") or similar.

https://docs.rust-embedded.org/book/start/panicking.html could help

hellow554 avatar Jun 11 '19 13:06 hellow554

Hmmm wonder why Result and expect() would cause this bloat compared with returning ints. Aren't they functionally equivalent?

That's one of the reasons I didn't include this for the article. Needs more investigation. For embedded systems, this kind of unexplained bloat could be showstoppers.

Thanks for your help! :-)

lupyuen avatar Jun 11 '19 14:06 lupyuen

Personally, I would not avoid using a Result even if that means, that by binary increases by a few kb.

warning: unused std::result::Result that must be used

That's the most amazing thing! If you forget to use the Result, the compiler will tell you. When you return an int, you don't need to handle it and you can easily swallow them. Also it doesn't tell you right away, what happened. It's just a convention that 0 means OK and any other values means Error. With Result you get it with the type system. Amazing!

hellow554 avatar Jun 11 '19 14:06 hellow554