Difference between TcpListener::bind('[::]:8000') and TcpListener::bind(':::8000')

If we run the following code:

use std::net::;

fn () {
    {
        let  = ::(":::8000").();
        !("Bound to: {}", .().());
    }

    {
        let  = ::("[::]:8000").();
        !("Bound to: {}", .().());
    }
}

We get the following output:

Bound to: [::]:8000
Bound to: [::]:8000

Therefore, it’s reasonable to believe that both calls run the same code path, and therefore will always work the same way. However, that’s not the case!

Breaking It Down

If we look at the signature for TcpListener::bind:

pub fn <: ToSocketAddrs>(: ) -> <TcpListener>;

We see that it takes a type implementing ToSocketAddrs:

pub trait  {
    type : < = SocketAddr>;

    // Required method
    fn (&) -> <::>;
}

For strings, the documentation states that

the string should be either a string representation of a SocketAddr as expected by its FromStr implementation or a string like <host_name>:<port> pair where <port> is a u16 value.

ToSocketAddrs

and looking at the source, we can see the following:

// accepts strings like 'localhost:12345'
#[(feature = "rust1", since = "1.0.0")]
impl ToSocketAddrs for  {
    type  = vec::IntoIter<SocketAddr>;
    fn (&) -> io::Result<vec::IntoIter<SocketAddr>> {
        // try to parse as a regular SocketAddr first
        if let () = .() {
            return (![].());
        }

        resolve_socket_addr(.()?)
    }
}

The implementation

  1. Checks if it can parse the string as a SocketAddr, and if so it returns it directly.
  2. Otherwise, it tries to resolve the address using platform interfaces, which on Linux is getaddrinfo(3).

We can verify that :::8000 cannot be parsed as a SocketAddr like so:

use std::net::;

fn () {
    {
        let  = ":::8000";
        !("{:?}", .::<>());
    }

    {
        let  = "[::]:8000";
        !("{:?}", .::<>());
    }
}

which prints

Err(AddrParseError(Socket))
Ok([::]:8000)

We can also verify that getaddrinfo is called on :::8000 using uftrace:

use std::net:: as _;

fn () {
    let  = ":::8000";
    !("{:?}", .());
}
$ cargo build && uftrace --no-pager -la ./target/debug/testing
Ok(IntoIter([[::]:8000]))
# DURATION     TID     FUNCTION
   3.379 us [ 20737] | poll(0x7ffc03010170, 3, 0) = 0;
   0.249 us [ 20737] | signal(SIGPIPE, 0x1) = 0;
   0.347 us [ 20737] | sysconf();
   0.055 us [ 20737] | pthread_self();
  84.702 us [ 20737] | pthread_getattr_np();
   0.040 us [ 20737] | pthread_attr_getstack();
   0.062 us [ 20737] | pthread_attr_destroy();
   0.238 us [ 20737] | sigaction(SIGSEGV, 0, 0x7ffc03010170) = 0;
   0.142 us [ 20737] | sigaction(SIGBUS, 0, 0x7ffc03010170) = 0;
   0.594 us [ 20737] | sigaltstack();
   0.058 us [ 20737] | getauxval();
   1.477 us [ 20737] | mmap64(0, 12288, PROT_WRITE|PROT_READ, MAP_STACK|MAP_ANON|MAP_PRIVATE, -1, 0) = 0x76e3984dd000;
   1.351 us [ 20737] | mprotect(0x76e3984dd000, 4096, PROT_NONE) = 0;
   0.352 us [ 20737] | sigaltstack();
   0.261 us [ 20737] | sigaction(SIGBUS, 0x7ffc03010170, 0) = 0;
   0.158 us [ 20737] | pthread_key_create();
   0.052 us [ 20737] | pthread_setspecific();
   0.101 us [ 20737] | bcmp();
   0.183 us [ 20737] | memcpy(0x7ffc0300fd68, 0x5f4accdaec9e, 2);
  57.689 us [ 20737] | getaddrinfo("::", "NULL", 0x7ffc0300fc80, 0x7ffc0300fd28) = 0;
   0.120 us [ 20737] | malloc(128) = 0x5f4ade24aad0;
   0.192 us [ 20737] | freeaddrinfo(0x5f4ade2853b0);
   0.081 us [ 20737] | malloc(1024) = 0x5f4ade1a6290;
   0.097 us [ 20737] | memcpy(0x5f4ade1a6290, 0x5f4accdaea72, 2);
   0.064 us [ 20737] | memcpy(0x5f4ade1a6292, 0x5f4accdb3182, 1);
   0.057 us [ 20737] | memcpy(0x5f4ade1a6293, 0x5f4accdae678, 8);
   0.052 us [ 20737] | memcpy(0x5f4ade1a629b, 0x5f4accdb3182, 1);
   0.046 us [ 20737] | memcpy(0x5f4ade1a629c, 0x5f4accdb306c, 1);
   0.051 us [ 20737] | memcpy(0x5f4ade1a629d, 0x5f4accdb306c, 1);
   0.050 us [ 20737] | memcpy(0x5f4ade1a629e, 0x5f4accdb3028, 2);
   0.069 us [ 20737] | memcpy(0x5f4ade1a62a0, 0x5f4accdb306d, 2);
   0.059 us [ 20737] | memcpy(0x5f4ade1a62a2, 0x7ffc0300f88c, 4);
   0.054 us [ 20737] | memcpy(0x5f4ade1a62a6, 0x5f4accdb3187, 1);
   0.055 us [ 20737] | memcpy(0x5f4ade1a62a7, 0x5f4accdb2f72, 1);
   0.048 us [ 20737] | memcpy(0x5f4ade1a62a8, 0x5f4accdb2f72, 1);
   0.045 us [ 20737] | memcpy(0x5f4ade1a62a9, 0x5f4accdaeca5, 1);
   4.425 us [ 20737] | write(1, 0x5f4ade1a6290, 26) = 26;
   0.061 us [ 20737] | memcpy(0x5f4ade1a6290, 0x5f4accdaeca6, 0);
   0.096 us [ 20737] | free(0x5f4ade24aad0);
   0.061 us [ 20737] | free(0x5f4ade1a6290);
   0.050 us [ 20737] | getauxval();
   0.127 us [ 20737] | sigaltstack();
   1.858 us [ 20737] | munmap(0x76e3984dd000, 12288) = 0;
   0.050 us [ 20737] | pthread_self();

Playing Around

We can also do a sneaky by patching getaddrinfo to filter certain addresses. Using the libc crate for convenience, let’s make a LD_PRELOADable library!

    Cargo.toml
[package]
name = "testing"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
libc = "0.2.169"
    src/lib.rs
use libc::{, , , , , };

#[]
unsafe extern "C" fn (
    : *const ,
    : *const ,
    : *const ,
    : *mut *mut ,
) ->  {
    // 1. Get the real `getaddrinfo`
    let  = libc::(libc::, c"getaddrinfo".());
    !(!.());

    // 2. Call the real `getaddrinfo` and return on error
    let mut : *mut  = std::ptr::();
    let : fn(
        *const ,
        *const ,
        *const ,
        *mut *mut ,
    ) ->  = std::mem::();
    let  = (, , , &raw mut );
    if  != 0 {
        return ;
    }

    let  = std::env::("SKIP_IPV4").(||  == "1");
    let  = std::env::("SKIP_IPV6").(||  == "1");

    // 3. Filter the returned `addrinfo` structs
    let mut  = &raw mut ;
    let mut : *mut  = ;
    while !.() {
        let  = (*).;

        if ( ==  && ) || ( ==  && ) {
            * = (*).;

            let : *mut  = ;
             = (*).;
            (*). = std::ptr::();
            ();
            continue;
        }
         = &raw mut (*).;
         = (*).;
    }
    * = ;

    
}

With the same example as before:

$ LD_PRELOAD=./target/debug/libtesting.so ./target/debug/testing
Ok(IntoIter([[::]:8000]))

$ SKIP_IPV6=1 LD_PRELOAD=./target/debug/libtesting.so ./target/debug/testing
Ok(IntoIter([]))

Success!

Conclusion

Is this relevant? Who knows! I’d like to hear if you know of any scenario where this makes significant difference…

src
fn main()
std::net::socket_addr
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>>

Converts this object to an iterator of resolved SocketAddrs.

The returned iterator might not actually yield any values depending on the outcome of any resolution performed.

Note that this function may block the current thread while resolution is performed.

std::net::socket_addr
pub trait ToSocketAddrs

A trait for objects which can be converted or resolved to one or more SocketAddr values.

This trait is used for generic address resolution when constructing network objects. By default it is implemented for the following types:

This trait allows constructing network objects like [TcpStream] or [UdpSocket] easily with values of various types for the bind/connection address. It is needed because sometimes one type is more appropriate than the other: for simple uses a string like "localhost:12345" is much nicer than manual construction of the corresponding SocketAddr, but sometimes SocketAddr value is the main source of the address, and converting it to some other type (e.g., a string) just for it to be converted back to SocketAddr in constructor methods is pointless.

Addresses returned by the operating system that are not IP addresses are silently ignored.

Examples

Creating a SocketAddr iterator that yields one item:

use std::net::{ToSocketAddrs, SocketAddr};

let addr = SocketAddr::from(([127, 0, 0, 1], 443));
let mut addrs_iter = addr.to_socket_addrs().unwrap();

assert_eq!(Some(addr), addrs_iter.next());
assert!(addrs_iter.next().is_none());

Creating a SocketAddr iterator from a hostname:

use std::net::{SocketAddr, ToSocketAddrs};

// assuming 'localhost' resolves to 127.0.0.1
let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443))));
assert!(addrs_iter.next().is_none());

// assuming 'foo' does not resolve
assert!("foo:443".to_socket_addrs().is_err());

Creating a SocketAddr iterator that yields multiple items:

use std::net::{SocketAddr, ToSocketAddrs};

let addr1 = SocketAddr::from(([0, 0, 0, 0], 80));
let addr2 = SocketAddr::from(([127, 0, 0, 1], 443));
let addrs = vec![addr1, addr2];

let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap();

assert_eq!(Some(addr1), addrs_iter.next());
assert_eq!(Some(addr2), addrs_iter.next());
assert!(addrs_iter.next().is_none());

Attempting to create a SocketAddr iterator from an improperly formatted socket address &str (missing the port):

use std::io;
use std::net::ToSocketAddrs;

let err = "127.0.0.1".to_socket_addrs().unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);

[TcpStream::connect] is an example of an function that utilizes ToSocketAddrs as a trait bound on its parameter in order to accept different types:

use std::net::{TcpStream, Ipv4Addr};

let stream = TcpStream::connect(("127.0.0.1", 443));
// or
let stream = TcpStream::connect("127.0.0.1:443");
// or
let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443));
let addr: &str
std::net::tcp::TcpListener
pub fn bind<A>(addr: A) -> io::Result<TcpListener>
where
    A: ToSocketAddrs,

Creates a new TcpListener which will be bound to the specified address.

The returned listener is ready for accepting connections.

Binding with a port number of 0 will request that the OS assigns a port to this listener. The port allocated can be queried via the TcpListener::local_addr method.

The address type can be any implementor of ToSocketAddrs trait. See its documentation for concrete examples.

If addr yields multiple addresses, bind will be attempted with each of the addresses until one succeeds and returns the listener. If none of the addresses succeed in creating a listener, the error returned from the last attempt (the last address) is returned.

Examples

Creates a TCP listener bound to 127.0.0.1:80:

use std::net::TcpListener;

let listener = TcpListener::bind("127.0.0.1:80").unwrap();

Creates a TCP listener bound to 127.0.0.1:80. If that fails, create a TCP listener bound to 127.0.0.1:443:

use std::net::{SocketAddr, TcpListener};

let addrs = [
    SocketAddr::from(([127, 0, 0, 1], 80)),
    SocketAddr::from(([127, 0, 0, 1], 443)),
];
let listener = TcpListener::bind(&addrs[..]).unwrap();

Creates a TCP listener bound to a port assigned by the operating system at 127.0.0.1.

use std::net::TcpListener;

let socket = TcpListener::bind("127.0.0.1:0").unwrap();
let ret: i32
s: OsString
let mut rest: *mut addrinfo
core::iter::traits::iterator::Iterator
pub type Item

The type of the elements being iterated over.

let addr: {unknown}
libc::unix::linux_like
pub struct addrinfo {
    pub ai_flags: i32,
    pub ai_family: i32,
    pub ai_socktype: i32,
    pub ai_protocol: i32,
    pub ai_addrlen: u32,
    /* … */
}
std::net::tcp
pub struct TcpListener(TcpListener)

A TCP socket server, listening for connections.

After creating a TcpListener by [bind]ing it to a socket address, it listens for incoming TCP connections. These can be accepted by calling [accept] or by iterating over the Incoming iterator returned by incoming.

The socket will be closed when the value is dropped.

The Transmission Control Protocol is specified in IETF RFC 793.

Examples

use std::net::{TcpListener, TcpStream};

fn handle_client(stream: TcpStream) {
    // ...
}

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:80")?;

    // accept connections and process them serially
    for stream in listener.incoming() {
        handle_client(stream?);
    }
    Ok(())
}
core::str
pub fn parse<F>(&self) -> Result<F, F::Err>
where
    F: FromStr,

Parses this string slice into another type.

Because parse is so general, it can cause problems with type inference. As such, parse is one of the few times you’ll see the syntax affectionately known as the ‘turbofish’: ::<>. This helps the inference algorithm understand specifically which type you’re trying to parse into.

parse can parse into any type that implements the FromStr trait.

Errors

Will return [Err] if it’s not possible to parse this string slice into the desired type.

Examples

Basic usage:

let four: u32 = "4".parse().unwrap();

assert_eq!(4, four);

Using the ‘turbofish’ instead of annotating four:

let four = "4".parse::<u32>();

assert_eq!(Ok(4), four);

Failing to parse:

let nope = "j".parse::<u32>();

assert!(nope.is_err());
libc::unix::<extern>
pub unsafe fn freeaddrinfo(res: *mut addrinfo)
src
pub trait ToSocketAddrs
addr: A
libc::unix
pub type c_int = i32
core::iter::traits::iterator
pub trait Iterator

A trait for dealing with iterators.

This is the main iterator trait. For more about the concept of iterators generally, please see the [module-level documentation]. In particular, you may want to know how to implement Iterator.

src
unsafe extern "C" fn getaddrinfo(node: *const c_char, service: *const c_char, hints: *const addrinfo, wrapped_rest: *mut *mut addrinfo) -> c_int
let listener: TcpListener
core::option::Option
impl<T> Option<T>
pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool

Returns true if the option is a Some and the value inside of it matches a predicate.

Examples

let x: Option<u32> = Some(2);
assert_eq!(x.is_some_and(|x| x > 1), true);

let x: Option<u32> = Some(0);
assert_eq!(x.is_some_and(|x| x > 1), false);

let x: Option<u32> = None;
assert_eq!(x.is_some_and(|x| x > 1), false);
let mut current: *mut addrinfo
let wrapped_fn: fn(*const i8, *const i8, *const addrinfo, *mut *mut addrinfo) -> i32
core::result
pub enum Result<T, E> {
    Ok( /* … */ ),
    Err( /* … */ ),
}

Result is a type that represents either success (Ok) or failure (Err).

See the documentation for details.

let mut rest_ptr: *mut *mut addrinfo
src
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>>
#[stable]

Valid forms are:

  • #[stable(feature = “name”, since = “version”)]
hints: *const addrinfo
Self: ?Sized
libc::unix::linux_like::linux
pub const RTLD_NEXT: *mut c_void = 0xFFFFFFFFFFFFFFFF as *mut c_void
libc::unix::<extern>
pub unsafe fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void
libc::unix::linux_like::linux::gnu::b64::x86_64
pub type c_char = i8
core::result::Result
Ok(T)

Contains the success value

std::net::tcp::TcpListener
pub fn local_addr(&self) -> io::Result<SocketAddr>

Returns the local socket address of this listener.

Examples

use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener};

let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
assert_eq!(listener.local_addr().unwrap(),
           SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080)));
alloc::macros
macro_rules! vec

Creates a [Vec] containing the arguments.

vec! allows Vecs to be defined with the same syntax as array expressions. There are two forms of this macro:

  • Create a [Vec] containing a given list of elements:
let v = vec![1, 2, 3];
assert_eq!(v[0], 1);
assert_eq!(v[1], 2);
assert_eq!(v[2], 3);
  • Create a [Vec] from a given element and size:
let v = vec![1; 3];
assert_eq!(v, [1, 1, 1]);

Note that unlike array expressions this syntax supports all elements which implement Clone and the number of elements doesn’t have to be a constant.

This will use clone to duplicate an expression, so one should be careful using this with types having a nonstandard Clone implementation. For example, vec![Rc::new(1); 5] will create a vector of five references to the same boxed integer value, not five references pointing to independently boxed integers.

Also, note that vec![expr; 0] is allowed, and produces an empty vector. This will still evaluate expr, however, and immediately drop the resulting value, so be mindful of side effects.

libc::unix::linux_like::addrinfo
pub ai_next: *mut addrinfo
std::macros
macro_rules! println

Prints to the standard output, with a newline.

On all platforms, the newline is the LINE FEED character (\n/U+000A) alone (no additional CARRIAGE RETURN (\r/U+000D)).

This macro uses the same syntax as format, but writes to the standard output instead. See [std::fmt] for more information.

The println! macro will lock the standard output on each call. If you call println! within a hot loop, this behavior may be the bottleneck of the loop. To avoid this, lock stdout with io::stdout().lock:

use std::io::{stdout, Write};

let mut lock = stdout().lock();
writeln!(lock, "hello world").unwrap();

Use println! only for the primary output of your program. Use [eprintln] instead to print error and progress messages.

See the formatting documentation in std::fmt for details of the macro argument syntax.

Panics

Panics if writing to [io::stdout] fails.

Writing to non-blocking stdout can cause an error, which will lead this macro to panic.

Examples

println!(); // prints just a newline
println!("hello there!");
println!("format {} arguments", "some");
let local_variable = "some";
println!("format {local_variable} arguments");
libc::unix::linux_like
pub const AF_INET: c_int = 2
core::ffi::c_str::CStr
pub const fn as_ptr(&self) -> *const c_char

Returns the inner pointer to this C string.

The returned pointer will be valid for as long as self is, and points to a contiguous region of memory terminated with a 0 byte to represent the end of the string.

The type of the returned pointer is *const c_char, and whether it’s an alias for *const i8 or *const u8 is platform-specific.

WARNING

The returned pointer is read-only; writing to it (including passing it to C code that writes to it) causes undefined behavior.

It is your responsibility to make sure that the underlying memory is not freed too early. For example, the following code will cause undefined behavior when ptr is used inside the unsafe block:

use std::ffi::CString;

// Do not do this:
let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
unsafe {
    // `ptr` is dangling
    *ptr;
}

This happens because the pointer returned by as_ptr does not carry any lifetime information and the CString is deallocated immediately after the CString::new("Hello").expect("CString::new failed").as_ptr() expression is evaluated. To fix the problem, bind the CString to a local variable:

use std::ffi::CString;

let hello = CString::new("Hello").expect("CString::new failed");
let ptr = hello.as_ptr();
unsafe {
    // `ptr` is valid because `hello` is in scope
    *ptr;
}

This way, the lifetime of the CString in hello encompasses the lifetime of ptr and the unsafe block.

src::ToSocketAddrs
pub type Iter: Iterator<Item = SocketAddr>
A
src
type Iter = vec::IntoIter<SocketAddr>
core::macros::builtin
macro_rules! assert

Asserts that a boolean expression is true at runtime.

This will invoke the panic macro if the provided expression cannot be evaluated to true at runtime.

Uses

Assertions are always checked in both debug and release builds, and cannot be disabled. See debug_assert for assertions that are not enabled in release builds by default.

Unsafe code may rely on assert! to enforce run-time invariants that, if violated could lead to unsafety.

Other use-cases of assert! include testing and enforcing run-time invariants in safe code (whose violation cannot result in unsafety).

Custom Messages

This macro has a second form, where a custom panic message can be provided with or without arguments for formatting. See std::fmt for syntax for this form. Expressions used as format arguments will only be evaluated if the assertion fails.

Examples

// the panic message for these assertions is the stringified value of the
// expression given.
assert!(true);

fn some_computation() -> bool { true } // a very simple function

assert!(some_computation());

// assert with a custom message
let x = true;
assert!(x, "x wasn't true!");

let a = 3; let b = 27;
assert!(a + b == 30, "a = {}, b = {}", a, b);
core::result::Result
impl<T, E> Result<T, E>
pub fn unwrap(self) -> T
where
    E: fmt::Debug,

Returns the contained Ok value, consuming the self value.

Because this function may panic, its use is generally discouraged. Instead, prefer to use pattern matching and handle the Err case explicitly, or call [unwrap_or], [unwrap_or_else], or [unwrap_or_default].

Panics

Panics if the value is an Err, with a panic message provided by the Err’s value.

Examples

Basic usage:

let x: Result<u32, &str> = Ok(2);
assert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err("emergency failure");
x.unwrap(); // panics with `emergency failure`
core::intrinsics
pub const unsafe fn transmute<Src, Dst>(_src: Src) -> Dst

Reinterprets the bits of a value of one type as another type.

Both types must have the same size. Compilation will fail if this is not guaranteed.

transmute is semantically equivalent to a bitwise move of one type into another. It copies the bits from the source value into the destination value, then forgets the original. Note that source and destination are passed by-value, which means if Src or Dst contain padding, that padding is not guaranteed to be preserved by transmute.

Both the argument and the result must be valid at their given type. Violating this condition leads to undefined behavior. The compiler will generate code assuming that you, the programmer, ensure that there will never be undefined behavior. It is therefore your responsibility to guarantee that every value passed to transmute is valid at both types Src and Dst. Failing to uphold this condition may lead to unexpected and unstable compilation results. This makes transmute incredibly unsafe. transmute should be the absolute last resort.

Because transmute is a by-value operation, alignment of the transmuted values themselves is not a concern. As with any other function, the compiler already ensures both Src and Dst are properly aligned. However, when transmuting values that point elsewhere (such as pointers, references, boxes…), the caller has to ensure proper alignment of the pointed-to values.

The nomicon has additional documentation.

Transmutation between pointers and integers

Special care has to be taken when transmuting between pointers and integers, e.g. transmuting between *const () and usize.

Transmuting pointers to integers in a const context is undefined behavior, unless the pointer was originally created from an integer. (That includes this function specifically, integer-to-pointer casts, and helpers like dangling, but also semantically-equivalent conversions such as punning through repr(C) union fields.) Any attempt to use the resulting value for integer operations will abort const-evaluation. (And even outside const, such transmutation is touching on many unspecified aspects of the Rust memory model and should be avoided. See below for alternatives.)

Transmuting integers to pointers is a largely unspecified operation. It is likely not equivalent to an as cast. Doing non-zero-sized memory accesses with a pointer constructed this way is currently considered undefined behavior.

All this also applies when the integer is nested inside an array, tuple, struct, or enum. However, MaybeUninit<usize> is not considered an integer type for the purpose of this section. Transmuting *const () to MaybeUninit<usize> is fine—but then calling assume_init() on that result is considered as completing the pointer-to-integer transmute and thus runs into the issues discussed above.

In particular, doing a pointer-to-integer-to-pointer roundtrip via transmute is not a lossless process. If you want to round-trip a pointer through an integer in a way that you can get back the original pointer, you need to use as casts, or replace the integer type by MaybeUninit<$int> (and never call assume_init()). If you are looking for a way to store data of arbitrary type, also use MaybeUninit<T> (that will also handle uninitialized memory due to padding). If you specifically need to store something that is “either an integer or a pointer”, use *mut (): integers can be converted to pointers and back without any loss (via as casts or via transmute).

Examples

There are a few things that transmute is really useful for.

Turning a pointer into a function pointer. This is not portable to machines where function pointers and data pointers have different sizes.

fn foo() -> i32 {
    0
}
// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
// This avoids an integer-to-pointer `transmute`, which can be problematic.
// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
let pointer = foo as *const ();
let function = unsafe {
    std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);

Extending a lifetime, or shortening an invariant lifetime. This is advanced, very unsafe Rust!

struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
                                             -> &'b mut R<'c> {
    std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}

Alternatives

Don’t despair: many uses of transmute can be achieved through other means. Below are common applications of transmute which can be replaced with safer constructs.

Turning raw bytes ([u8; SZ]) into u32, f64, etc.:

let raw_bytes = [0x78, 0x56, 0x34, 0x12];

let num = unsafe {
    std::mem::transmute::<[u8; 4], u32>(raw_bytes)
};

// use `u32::from_ne_bytes` instead
let num = u32::from_ne_bytes(raw_bytes);
// or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);

Turning a pointer into a usize:

let ptr = &0;
let ptr_num_transmute = unsafe {
    std::mem::transmute::<&i32, usize>(ptr)
};

// Use an `as` cast instead
let ptr_num_cast = ptr as *const i32 as usize;

Note that using transmute to turn a pointer to a usize is (as noted above) undefined behavior in const contexts. Also outside of consts, this operation might not behave as expected – this is touching on many unspecified aspects of the Rust memory model. Depending on what the code is doing, the following alternatives are preferable to pointer-to-integer transmutation:

  • If the code just wants to store data of arbitrary type in some buffer and needs to pick a type for that buffer, it can use MaybeUninit.
  • If the code actually wants to work on the address the pointer points to, it can use as casts or ptr.addr().

Turning a *mut T into a &mut T:

let ptr: *mut i32 = &mut 0;
let ref_transmuted = unsafe {
    std::mem::transmute::<*mut i32, &mut i32>(ptr)
};

// Use a reborrow instead
let ref_casted = unsafe { &mut *ptr };

Turning a &mut T into a &mut U:

let ptr = &mut 0;
let val_transmuted = unsafe {
    std::mem::transmute::<&mut i32, &mut u32>(ptr)
};

// Now, put together `as` and reborrowing - note the chaining of `as`
// `as` is not transitive
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };

Turning a &str into a &[u8]:

// this is not a good way to do this.
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);

// You could use `str::as_bytes`
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);

// Or, just use a byte string, if you have control over the string
// literal
assert_eq!(b"Rust", &[82, 117, 115, 116]);

Turning a Vec<&T> into a Vec<Option<&T>>.

To transmute the inner type of the contents of a container, you must make sure to not violate any of the container’s invariants. For Vec, this means that both the size and alignment of the inner types have to match. Other containers might rely on the size of the type, alignment, or even the TypeId, in which case transmuting wouldn’t be possible at all without violating the container invariants.

let store = [0, 1, 2, 3];
let v_orig = store.iter().collect::<Vec<&i32>>();

// clone the vector as we will reuse them later
let v_clone = v_orig.clone();

// Using transmute: this relies on the unspecified data layout of `Vec`, which is a
// bad idea and could cause Undefined Behavior.
// However, it is no-copy.
let v_transmuted = unsafe {
    std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_clone)
};

let v_clone = v_orig.clone();

// This is the suggested, safe way.
// It may copy the entire vector into a new one though, but also may not.
let v_collected = v_clone.into_iter()
                         .map(Some)
                         .collect::<Vec<Option<&i32>>>();

let v_clone = v_orig.clone();

// This is the proper no-copy, unsafe way of "transmuting" a `Vec`, without relying on the
// data layout. Instead of literally calling `transmute`, we perform a pointer cast, but
// in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`),
// this has all the same caveats. Besides the information provided above, also consult the
// [`from_raw_parts`] documentation.
let v_from_raw = unsafe {
    // Ensure the original vector is not dropped.
    let mut v_clone = std::mem::ManuallyDrop::new(v_clone);
    Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>,
                        v_clone.len(),
                        v_clone.capacity())
};

Implementing split_at_mut:

use std::{slice, mem};

// There are multiple ways to do this, and there are multiple problems
// with the following (transmute) way.
fn split_at_mut_transmute<T>(slice: &mut [T], mid: usize)
                             -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice);
        // first: transmute is not type safe; all it checks is that T and
        // U are of the same size. Second, right here, you have two
        // mutable references pointing to the same memory.
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// This gets rid of the type safety problems; `&mut *` will *only* give
// you a `&mut T` from a `&mut T` or `*mut T`.
fn split_at_mut_casts<T>(slice: &mut [T], mid: usize)
                         -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = &mut *(slice as *mut [T]);
        // however, you still have two mutable references pointing to
        // the same memory.
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// This is how the standard library does it. This is the best method, if
// you need to do something like this
fn split_at_stdlib<T>(slice: &mut [T], mid: usize)
                      -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let ptr = slice.as_mut_ptr();
        // This now has three mutable references pointing at the same
        // memory. `slice`, the rvalue ret.0, and the rvalue ret.1.
        // `slice` is never used after `let ptr = ...`, and so one can
        // treat it as "dead", and therefore, you only have two real
        // mutable slices.
        (slice::from_raw_parts_mut(ptr, mid),
         slice::from_raw_parts_mut(ptr.add(mid), len - mid))
    }
}
src::ToSocketAddrs
pub trait ToSocketAddrs
pub fn to_socket_addrs(&self) -> Result<Self::Iter>
libc::unix::linux_like
pub const AF_INET6: c_int = 10 (0xA)
core::ptr::mut_ptr
impl<T> *mut T
pub const fn is_null(self) -> bool
where
    // Bounds from impl:
    T: ?Sized,

Returns true if the pointer is null.

Note that unsized types have many possible null pointers, as only the raw data pointer is considered, not their length, vtable, etc. Therefore, two pointers that are null may still not compare equal to each other.

Behavior during const evaluation

When this function is used during const evaluation, it may return false for pointers that turn out to be null at runtime. Specifically, when a pointer to some memory is offset beyond its bounds in such a way that the resulting pointer is null, the function will still return false. There is no way for CTFE to know the absolute position of that memory, so we cannot tell if the pointer is null or not.

Examples

let mut s = [1, 2, 3];
let ptr: *mut u32 = s.as_mut_ptr();
assert!(!ptr.is_null());
let wrapped_ptr: *mut c_void
src
pub fn bind<A>(addr: A) -> Result<TcpListener>
where
    A: ToSocketAddrs,
core::net::socket_addr
pub enum SocketAddr {
    V4( /* … */ ),
    V6( /* … */ ),
}

An internet socket address, either IPv4 or IPv6.

Internet socket addresses consist of an [IP address], a 16-bit port number, as well as possibly some version-dependent additional information. See SocketAddrV4’s and SocketAddrV6’s respective documentation for more details.

The size of a SocketAddr instance may vary depending on the target operating system.

Examples

use std::net::{IpAddr, Ipv4Addr, SocketAddr};

let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);

assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
assert_eq!(socket.port(), 8080);
assert_eq!(socket.is_ipv4(), true);
wrapped_rest: *mut *mut addrinfo
node: *const i8
std::env
pub fn var_os<K>(key: K) -> Option<OsString>
where
    K: AsRef<OsStr>,

Fetches the environment variable key from the current process, returning None if the variable isn’t set or if there is another error.

It may return None if the environment variable’s name contains the equal sign character (=) or the NUL character.

Note that this function will not check if the environment variable is valid Unicode. If you want to have an error on invalid UTF-8, use the var function instead.

Examples

use std::env;

let key = "HOME";
match env::var_os(key) {
    Some(val) => println!("{key}: {val:?}"),
    None => println!("{key} is not defined in the environment.")
}

If expecting a delimited variable (such as PATH), split_paths can be used to separate items.

libc::unix::linux_like::addrinfo
pub ai_family: i32
let skip_ipv6: bool
self: &str
service: *const i8
let skip_ipv4: bool
core::iter::traits::collect::IntoIterator
pub trait IntoIterator
pub fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value.

See the [module-level documentation] for more.

Examples

let v = [1, 2, 3];
let mut iter = v.into_iter();

assert_eq!(Some(1), iter.next());
assert_eq!(Some(2), iter.next());
assert_eq!(Some(3), iter.next());
assert_eq!(None, iter.next());
let to_skip: *mut addrinfo
#[no_mangle]

Valid forms are:

  • #[no_mangle]
str

String slices.

See also the std::str module.

The str type, also called a ‘string slice’, is the most primitive string type. It is usually seen in its borrowed form, &str. It is also the type of string literals, &'static str.

Basic Usage

String literals are string slices:

let hello_world = "Hello, World!";

Here we have declared a string slice initialized with a string literal. String literals have a static lifetime, which means the string hello_world is guaranteed to be valid for the duration of the entire program. We can explicitly specify hello_world’s lifetime as well:

let hello_world: &'static str = "Hello, world!";

Representation

A &str is made up of two components: a pointer to some bytes, and a length. You can look at these with the [as_ptr] and [len] methods:

use std::slice;
use std::str;

let story = "Once upon a time...";

let ptr = story.as_ptr();
let len = story.len();

// story has nineteen bytes
assert_eq!(19, len);

// We can re-build a str out of ptr and len. This is all unsafe because
// we are responsible for making sure the two components are valid:
let s = unsafe {
    // First, we build a &[u8]...
    let slice = slice::from_raw_parts(ptr, len);

    // ... and then convert that slice into a string slice
    str::from_utf8(slice)
};

assert_eq!(s, Ok(story));

Note: This example shows the internals of &str. unsafe should not be used to get a string slice under normal circumstances. Use as_str instead.

Invariant

Rust libraries may assume that string slices are always valid UTF-8.

Constructing a non-UTF-8 string slice is not immediate undefined behavior, but any function called on a string slice may assume that it is valid UTF-8, which means that a non-UTF-8 string slice can lead to undefined behavior down the road.

let family: i32
core::ptr
pub const fn null_mut<T>() -> *mut T
where
    T: ?Sized + Thin,

Creates a null mutable raw pointer.

This function is equivalent to zero-initializing the pointer: MaybeUninit::<*mut T>::zeroed().assume_init(). The resulting pointer has the address 0.

Examples

use std::ptr;

let p: *mut i32 = ptr::null_mut();
assert!(p.is_null());
assert_eq!(p as usize, 0); // this pointer has the address 0
self: &Self
core::convert::TryInto
pub trait TryInto<T>
pub fn try_into(self) -> Result<T, Self::Error>
where
    // Bounds from trait:
    Self: Sized,

Performs the conversion.