0

I am working on a client server application where i send messages to / from each as protocol buffers using the rust crate prost. currently i have a generic send_message function that can take any struct that implements the prost::Message trait. I'm trying to create a similar function for reading a type that implements prost::Message. I believe the issue is that decode which is a method for types that implement prost::Message is an associated function and not an instance method. the actual instance of the object that implements prost::Message is created within the function.

Im wondering if there is a way to make a generic read_message function that can accept any type that implements prost::Message and use the associated function decode on that type.

This is my current send_message function:

pub fn send_message<M>(message: M, stream: &mut TcpStream) -> io::Result<()>
where
    M: Message,
{
    let length = message.encoded_len() as i32;
    let mut buf: Vec<u8> = Vec::with_capacity(length as usize);
    message.encode(&mut buf)?;
    stream.write_all(&length.to_le_bytes())?;
    stream.write_all(&buf)?;
    Ok(())
}

and right now i have two read_TYPE_message functions:

pub fn read_response_message(stream: &mut TcpStream) -> io::Result<message::Response> {
    let mut len_buf = [0u8; 4];
    stream.read_exact(&mut len_buf)?;
    let len = i32::from_le_bytes(len_buf);
    println!("Incoming message length: {}", len);
    let mut buf = vec![0u8; len as usize];
    stream.read_exact(&mut buf)?;
    let response = message::Response::decode(&mut buf.as_slice())?;
    Ok(response)
}

fn read_request_message(stream: &mut TcpStream) -> io::Result<message::Request> {
    let mut len_buf = [0u8; 4];
    stream.read_exact(&mut len_buf)?;
    let len = i32::from_le_bytes(len_buf);
    println!("Incoming message length: {}", len);
    let mut buf = vec![0u8; len as usize];
    stream.read_exact(&mut buf)?;
    let user_input = message::Request::decode(&mut buf.as_slice())?;
    Ok(user_input)
}

When i tried the below:

fn read_message<M: Message>(msg: M, stream: &mut TcpStream) -> io::Result<M> {
    let mut len_buf = [0u8; 4];
    stream.read_exact(&mut len_buf)?;
    let len = i32::from_le_bytes(len_buf);
    println!("Incoming message length: {}", len);
    let mut buf = vec![0u8; len as usize];
    stream.read_exact(&mut buf)?;
    let pb_msg = msg::decode(&mut buf.as_slice())?;
    Ok(pb_msg)
}

I get the error

failed to resolve: use of undeclared crate or module `msg` use of undeclared crate or module `msg`

i was hoping to be able use the function something like this:

let input = read_message(message::Request, &mut stream)?;
let input = read_message(message::Response, &mut stream)?;
Herohtar
  • 5,347
  • 4
  • 31
  • 41
matthewmturner
  • 566
  • 7
  • 21
  • 2
    `msg::decode(...)`, you cannot call static method over variable, compiler thinks it as a path, this explains the error message you've got. You need to it like this `M::decode(&mut buf.as_slice)` – Ömer Erden Sep 24 '21 at 20:16
  • thank you @ÖmerErden, i believe this gets me closer. now im struggling to see how i use that approach to then dispatch to returning the appropriate type that implements `M` i.e. `M::decode(&mut buf.as_slice)` can return either a `Response` or a `Request` – matthewmturner Sep 24 '21 at 20:38
  • 1
    You are already in generic scope, your concrete type is `M` which can be `Request` or `Response` not both, so either define your type like `let pb_msg: M = ...` or let compiler infer it. Here is a similar code to yours [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c787eb0fad5658ab29d81e42c78c6aeb) – Ömer Erden Sep 24 '21 at 20:39
  • that worked. thanks so much! – matthewmturner Sep 24 '21 at 20:49

0 Answers0