rust-embedded-community / embedded-nal

An Embedded Network Abstraction Layer
Apache License 2.0
177 stars 25 forks source link

need an object that can impl uWrite #64

Closed mutantbob closed 2 years ago

mutantbob commented 2 years ago

I was refactoring some code to use embedded-nal instead of a custom driver and found that embedded-nal does not have any implementation of ufmt::uWrite.

So I created a struct StackAndSocket which combines mutable references to the TcpClientStack and TcpSocket so that it can impl uWrite.

https://github.com/mutantbob/embedded-nal/commit/e4a65f48558c433f09ba0853a2d7a0f06a8be294

It is not meant to exist for a long time; you just create it for a quick burst of activity, and then drop it. I use it like this:

        if let Ok(client) = client.as_mut() {
            buffer.flush(&mut ethernet.with_socket(client), spi)
        } else {
            buffer.discard()
        }

I suspect this idea has overlap with https://github.com/rust-embedded-community/embedded-nal/issues/53 ,

ryan-summers commented 2 years ago

I think it would be helpful to understand why this would be needed. What's the use case for impl uWrite? I personally don't use ufmt and don't have any need for it. Is there a purpose that it should be pulled into this library, or can it be kept external?

mutantbob commented 2 years ago

I use uWrite to construct HTTP PUT requests. I also use it to transmit sensor readings. I can understand if uwrite is gated behind a feature flag. I added StackAndSocket because I wanted the function with_socket(), although I suspect that method could be added using a custom trait . I'll have to experiment with that.

ryan-summers commented 2 years ago

Wouldn't you be better suited to write those ufmt objects into some intermediary buffer before sending them for transmission to the stack?

E.g.

let mut buffer = heapless::String::new();
uwrite!(&mut buffer, "my_http_format_string", my_http_data).unwrap();
tcp_stack.send(tcp_sock, buffer.as_slice()).unwrap()

I don't see why the network stack itself has to be some type of writable stream outside of the potential desire to avoid buffering?

mutantbob commented 2 years ago

I tried your example:

let mut x = embedded_nal::heapless::String::new();
uwrite!(&mut x, "bacon {}", 42);

I get the following error

error[E0599]: no method named `do_as_formatter` found for mutable reference `&mut embedded_nal::heapless::String<{_: usize}>` in the current scope
   --> src/main.rs:585:5
    |
585 |     uwrite!(&mut x, "bacon {}", 42);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `&mut embedded_nal::heapless::String<{_: usize}>`
    | 
   ::: /home/thoth/.cargo/git/checkouts/heapless-c15edde3bc74f47c/8460024/src/string.rs:8:1
    |
8   | pub struct String<const N: usize> {
    | ---------------------------------
    | |
    | doesn't satisfy `_: UnstableDoAsFormatter`
    | doesn't satisfy `embedded_nal::heapless::String<{_: usize}>: uWrite`
    |
    = note: the method `do_as_formatter` exists but the following trait bounds were not satisfied:
            `embedded_nal::heapless::String<{_: usize}>: uWrite`
            which is required by `embedded_nal::heapless::String<{_: usize}>: UnstableDoAsFormatter`
            `&mut embedded_nal::heapless::String<{_: usize}>: uWrite`
            which is required by `&mut embedded_nal::heapless::String<{_: usize}>: UnstableDoAsFormatter`
            `str: uWrite`
            which is required by `str: UnstableDoAsFormatter`
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

It turns out that the custom trait is a workable solution without modifying embedded-nal . https://github.com/mutantbob/embedded-nal-plus/blob/master/src/lib.rs

ryan-summers commented 2 years ago

Using a custom trait will break any kind of cross-compatibility with existing embedded-nal libraries - I would not recommend going that route. I think there are means to work around this without a custom trait and without a custom NAL.

mutantbob commented 2 years ago

I'm interested in seeing an example of an existing embedded-nal library that would be incompatible with how I designed the custom trait. If anyone can construct one, it would enhance my understanding of Rust.

eldruin commented 2 years ago

I agree with @ryan-summers and do not think embedded-nal would be the place to provide implementations for ufmt traits. IIUC there is nothing in ufmt that embedded-nal would inherently benefit from and I would keep the rather short adapter code outside of this crate as to avoid non-essential dependencies. If you disagree, you are welcome to provide a compelling use case and reopen this issue. Closing.