Create buffer borrowed by embedded TcpSocket

I try to create a wrapper around the embassy TcpSocket used in rp pico which is in #[no_std] code which contains the TcpSocket and the buffers it uses:

pub struct MyTcpClient<'a> {
    recv_buf: [u8; 1024],
    send_buf: [u8; 1024],
    tcp_socket :TcpSocket<'a>,
    // other data
}

impl<'a> MyTcpClient<'a> {
    pub fn new(
        stack: &'static Stack<cyw43::NetDriver<'static>>,
    ) -> Self {
        let mut recv_buf = [0_u8; 1024];
        let mut send_buf = [0_u8; 1024];

        let tcp_socket = TcpSocket::new(stack, &mut recv_buf, &mut send_buf);
        Self { recv_buf, send_buf, tcp_socket }
    }
}

However the complier complains, and reading the errors it seem very resonable.

error[E0515]: cannot return value referencing local variable `send_buf`
  --> src/HAMqttDevice.rs:97:9
   |
96 |         let tcp_socket = TcpSocket::new(stack, &mut recv_buf, &mut send_buf);
   |                                                               ------------- `send_buf` is borrowed here
97 |         Self { recv_buf, send_buf, tcp_socket }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `recv_buf`
  --> src/HAMqttDevice.rs:97:9
   |
96 |         let tcp_socket = TcpSocket::new(stack, &mut recv_buf, &mut send_buf);
   |                                                ------------- `recv_buf` is borrowed here
97 |         Self { recv_buf, send_buf, tcp_socket }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0503]: cannot use `recv_buf` because it was mutably borrowed
  --> src/HAMqttDevice.rs:97:16
   |
89 | impl<'a> HaMqttClient<'a> {
   |      -- lifetime `'a` defined here
...
96 |         let tcp_socket = TcpSocket::new(stack, &mut recv_buf, &mut send_buf);
   |                                                ------------- `recv_buf` is borrowed here
97 |         Self { recv_buf, send_buf, tcp_socket }
   |         -------^^^^^^^^------------------------
   |         |      |
   |         |      use of borrowed `recv_buf`
   |         returning this value requires that `recv_buf` is borrowed for `'a`

error[E0503]: cannot use `send_buf` because it was mutably borrowed
  --> src/HAMqttDevice.rs:97:26
   |
89 | impl<'a> HaMqttClient<'a> {
   |      -- lifetime `'a` defined here
...
96 |         let tcp_socket = TcpSocket::new(stack, &mut recv_buf, &mut send_buf);
   |                                                               ------------- `send_buf` is borrowed here
97 |         Self { recv_buf, send_buf, tcp_socket }
   |         -----------------^^^^^^^^--------------
   |         |                |
   |         |                use of borrowed `send_buf`
   |         returning this value requires that `send_buf` is borrowed for `'a`

I could solve this by just adding the buffers as an input argument and add a lifetime instead. But I don't like the idea of just forwarding the borrow. It makes the new() function a bit cluttered and I don't see the reason for them to be created before hand as MyTcpClient could contain the buffers.

pub struct MyTcpClient<'a> {
    tcp_socket :TcpSocket<'a>,
    // other data
}

impl<'a> MyTcpClient<'a> {
    pub fn new(
        stack: &'static Stack<cyw43::NetDriver<'static>>,
        recv_buf: &'a mut [u8],
        send_buf: &'a mut [u8],
    ) -> Self {
        let tcp_socket = TcpSocket::new(stack, recv_buf, send_buf);
        Self { tcp_socket }
    }

So how would I be able to do this. Have the nice new() function of the first code. But still have it compile.

Here you're trying to make a self-referential borrow where part of the struct is borrowing data owned by another part of the same struct, which is not possible in (safe) Rust. If forwarding the borrows works for you, that's the simplest way to solve it without having to use unnecessary unsafe code.

3 Likes

The references will dangle when MyTcpClient is moved (e.g. returned from new).

Even unsafe can't help unless you can also introduce indirection, such as boxing. (Even then it's notoriously difficult to make sound.)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.