Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing &str as &[u8]

Tags:

tcp

rust

I'm driving myself bonkers with this one, because judging by the documentation it should be painless: how do I cast a string into an &[u8] so I can send it over the wire via TCP or UDP? The bytes!() macro only appears to work on literals directly.

Here's the skeleton for a TCP app, cribbed entirely from other sources. Right now it works as an echo server. The stumbling block I'm having is figuring out how to print the &[u8] as a string on the terminal, or how to cast strings from io::stdin().read_line() into &[u8] to send over the wire, chat-style.

This fails to compile on a error: mismatched types: expected `&[u8]` but found `&str` (expected vector but found &str):

fn run_tcp_test_server(listen_addr: SocketAddr) {
    let mut acceptor = TcpListener::bind(listen_addr).listen().unwrap();
    println("[ INFO ] listener is ready.");

    loop {
        let stream = Cell::new(acceptor.accept().unwrap());
        do spawn {
            println("[ INFO ] got a request.");

            let mut stream = stream.take();

            let mut my_read_buff = ~[0, ..1024];
            match stream.read(my_read_buff) {
                Some(n) => {
                    stream.write(my_read_buff.slice_to(n));
                },
                _ => ()
            }

            let out_msg = "Hello World\r\n";
            stream.write(out_msg);
        }
    }
}

I also don't think the let mut my_read_buff = ~[0, ..1024] can possibly be right, that seems like a C-style overflow waiting to happen, which I thought Rust was supposed to fix.

I'm pretty confused by this, my understanding is that &str is &[u8] and I'm getting snagged by the type system, but can't get past this. I've tried to implement some of the functions from the std::str::raw in unsafe blocks but had no luck there, either.

like image 384
ispilledthejava Avatar asked Mar 21 '23 03:03

ispilledthejava


1 Answers

&str has the same representation as &[u8], but it has an additional invariant: the contents is always valid UTF-8, hence it is not the same type, i.e. you have to explicitly "cast" between them (a little similar to u8 and i8 having the same representation but requiring an explicit cast to use them in place). This "cast" is done with .as_bytes() to go from &str&[u8], and std::str::from_utf8 for the other direction (on 0.8, the reverse cast is from_utf8_slice FWIW). The &str&[u8] cast is very, very cheap; with optimisations on it is literally zero-cost (the other direction has to check for valid UTF-8, and hence is O(n)), and with them off it is still a function call to a function with a very small number of instructions.

Hence, you need:

stream.write(out_msg.as_bytes());

(If out_msg is always the same message you can actually use the b prefix to get a compile-time byte literal:

stream.write(b"Hello World\r\n")

)


I also don't think the let mut my_read_buff = ~[0, ..1024] can possibly be right, that seems like a C-style overflow waiting to happen, which I thought Rust was supposed to fix.

No, vectors and slices have their length stored in them so the .read call knows how much space it is allowed to fill, and won't/can't write beyond the end of it.

(FWIW, if your buffer is always a fixed length, you can drop the ~, i.e. let mut my_read_buff = [0, ..1024];, so my_read_buf will have type [u8, .. 1024], i.e. a fixed length vector of 1024 u8's. This avoids the heap allocation.)

like image 103
huon Avatar answered Mar 28 '23 00:03

huon