Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust Type Inference Error

I'm writing a chat server over TCP as a learning project. I've been tinkering with the ws crate today, but I've come across an issue. This is the code I wrote, modifying their server example.

extern crate ws;
extern crate env_logger;

use ws::listen;

fn main() {
    // Setup logging
    env_logger::init().unwrap();

    // Listen on an address and call the closure for each connection
    if let Err(error) = listen("127.0.0.1:3012", |out| {
        let mut message: String;
        // The handler needs to take ownership of out, so we use move
        move |message| {
            message = message.trim();
            // Handle messages received on this connection
            println!("Server got message '{}'. ", message);

            // Use the out channel to send messages back
            out.send(message)
        }

    }) {
        // Inform the user of failure
        println!("Failed to create WebSocket due to {:?}", error);
    }
}

When I try compiling it I get an error:

error: the type of this value must be known in this context
  --> src/main.rs:15:23
   |
15 |             message = message.trim();
   |                       ^^^^^^^^^^^^^^

Why is this happening? How may I fix this?

like image 584
Bernardo Meurer Avatar asked Dec 05 '16 22:12

Bernardo Meurer


1 Answers

move |message| shadows the message variable you've declared outside the closure. So within the closure.. message is said to be a ws::Message ... except you've done this:

message = message.trim();

The compiler goes "oh no! trim()? That doesn't exist for ws::Message".. and so now it doesn't quite know what to do.

Option 1

The first fix involves delegating the trim() call to the client who sends the message.

The fix is to not make any assumptions about what the message is inside this closure. If you keep this:

move |message|

..but remove the trim() call, the compiler happily infers its type as ws::Message and will build:

if let Err(error) = listen("127.0.0.1:3012", |out| {
    // The handler needs to take ownership of out, so we use move
    move |message| {
        // --- REMOVED trim() call ---

        // Handle messages received on this connection
        println!("Server got message '{}'. ", message);

        // Use the out channel to send messages back
        out.send(message)
    }
}

This gives you the option of delegating the trim() call to the client instead.

Option 2

Option 2 involves inspecting the type of message you've received, and making sure you trim it only if it is text:

// The handler needs to take ownership of out, so we use move
move |mut message: ws::Message| {
    // Only do it if the Message is text
    if message.is_text() {
        message = ws::Message::Text(message.as_text().unwrap().trim().into());
    }

    // Handle messages received on this connection
    println!("Server got message '{}'. ", message);

    // Use the out channel to send messages back
    out.send(message)
}

This is perhaps a little more verbose than it needs to be.. but hopefully it shows you what the actual issue is with your original snippet of code.

like image 156
Simon Whitehead Avatar answered Nov 07 '22 04:11

Simon Whitehead