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

If we run the following code:

use ::::;

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 ::::;

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 :::: 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!

[package]
name = "testing"
version = "0.1.0"
edition = "2021"

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

[dependencies]
libc = "0.2.169"
use ::{, , , , , };

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

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

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

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

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

            let : *mut  = ;
             = (*).;
            (*). = ::::();
            ();
            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...

extern crate std

The Rust Standard Library

The Rust Standard Library is the foundation of portable Rust software, a set of minimal and battle-tested shared abstractions for the broader Rust ecosystem. It offers core types, like [Vec<T>] and [Option<T>], library-defined operations on language primitives, standard macros, [I/O] and [multithreading], among many other things.

std is available to all Rust crates by default. Therefore, the standard library can be accessed in use statements through the path std, as in use std::env.

How to read this documentation

If you already know the name of what you are looking for, the fastest way to find it is to use the search button at the top of the page.

Otherwise, you may want to jump to one of these useful sections:

If this is your first time, the documentation for the standard library is written to be casually perused. Clicking on interesting things should generally lead you to interesting places. Still, there are important bits you don’t want to miss, so read on for a tour of the standard library and its documentation!

Once you are familiar with the contents of the standard library you may begin to find the verbosity of the prose distracting. At this stage in your development you may want to press the “ Summary” button near the top of the page to collapse it into a more skimmable view.

While you are looking at the top of the page, also notice the “Source” link. Rust’s API documentation comes with the source code and you are encouraged to read it. The standard library source is generally high quality and a peek behind the curtains is often enlightening.

What is in the standard library documentation?

First of all, The Rust Standard Library is divided into a number of focused modules, all listed further down this page. These modules are the bedrock upon which all of Rust is forged, and they have mighty names like [std::slice] and [std::cmp]. Modules’ documentation typically includes an overview of the module along with examples, and are a smart place to start familiarizing yourself with the library.

Second, implicit methods on primitive types are documented here. This can be a source of confusion for two reasons:

  1. While primitives are implemented by the compiler, the standard libraryimplements methods directly on the primitive types (and it is the onlylibrary that does so), which are documented in the section on primitives.
  2. The standard library exports many modules with the same name as primitive types. These define additional items related to the primitivetype, but not the all-important methods.

So for example there is a page for the primitive type char that lists all the methods that can be called on characters (very useful), and there is a page for the module std::char that documents iterator and error types created by these methods (rarely useful).

Note the documentation for the primitives [str] and [T] (also called ‘slice’). Many method calls on String and [Vec<T>] are actually calls to methods on [str] and [T] respectively, via deref coercions.

Third, the standard library defines [The Rust Prelude], a small collection of items - mostly traits - that are imported into every module of every crate. The traits in the prelude are pervasive, making the prelude documentation a good entry point to learning about the library.

And finally, the standard library exports a number of standard macros, and lists them on this page (technically, not all of the standard macros are defined by the standard library - some are defined by the compiler - but they are documented here the same). Like the prelude, the standard macros are imported by default into all crates.

Contributing changes to the documentation

Check out the Rust contribution guidelines here. The source for this documentation can be found on GitHub in the ‘library/std/’ directory. To contribute changes, make sure you read the guidelines first, then submit pull-requests for your suggested changes.

Contributions are appreciated! If you see a part of the docs that can be improved, submit a PR, or chat with us first on Zulip #docs.

A Tour of The Rust Standard Library

The rest of this crate documentation is dedicated to pointing out notable features of The Rust Standard Library.

Containers and collections

The option and result modules define optional and error-handling types, [Option<T>] and [Result<T, E>]. The iter module defines Rust’s iterator trait, Iterator, which works with the for loop to access collections.

The standard library exposes three common ways to deal with contiguous regions of memory:

  • [Vec<T>] - A heap-allocated vector that is resizable at runtime.
  • [T; N] - An inline array with a fixed size at compile time.
  • [T] - A dynamically sized slice into any other kind of contiguousstorage, whether heap-allocated or not.

Slices can only be handled through some kind of pointer, and as such come in many flavors such as:

  • &[T] - shared slice
  • &mut [T] - mutable slice
  • Box<[T]> - owned slice

[str], a UTF-8 string slice, is a primitive type, and the standard library defines many methods for it. Rust [str]s are typically accessed as immutable references: &str. Use the owned String for building and mutating strings.

For converting to strings use the format macro, and for converting from strings use the [FromStr] trait.

Data may be shared by placing it in a reference-counted box or the [Rc] type, and if further contained in a [Cell] or [RefCell], may be mutated as well as shared. Likewise, in a concurrent setting it is common to pair an atomically-reference-counted box, [Arc], with a [Mutex] to get the same effect.

The collections module defines maps, sets, linked lists and other typical collection types, including the common [HashMap<K, V>].

Platform abstractions and I/O

Besides basic data types, the standard library is largely concerned with abstracting over differences in common platforms, most notably Windows and Unix derivatives.

Common types of I/O, including [files], [TCP], and [UDP], are defined in the io, fs, and net modules.

The thread module contains Rust’s threading abstractions. sync contains further primitive shared memory types, including [atomic], [mpmc] and [mpsc], which contains the channel types for message passing.

Use before and after main()

Many parts of the standard library are expected to work before and after main(); but this is not guaranteed or ensured by tests. It is recommended that you write your own tests and run them on each platform you wish to support. This means that use of std before/after main, especially of features that interact with the OS or global state, is exempted from stability and portability guarantees and instead only provided on a best-effort basis. Nevertheless bug reports are appreciated.

On the other hand core and alloc are most likely to work in such environments with the caveat that any hookable behavior such as panics, oom handling or allocators will also depend on the compatibility of the hooks.

Some features may also behave differently outside main, e.g. stdio could become unbuffered, some panics might turn into aborts, backtraces might not get symbolicated or similar.

Non-exhaustive list of known limitations:

  • after-main use of thread-locals, which also affects additional features:
  • under UNIX, before main, file descriptors 0, 1, and 2 may be unchanged(they are guaranteed to be open during main,and are opened to /dev/null O_RDWR if they weren’t open on program start)
std
pub mod net

Networking primitives for TCP/UDP communication.

This module provides networking functionality for the Transmission Control and User Datagram Protocols, as well as types for IP and socket addresses and functions related to network properties.

Organization

Rust disables inheritance of socket objects to child processes by default when possible. For example, through the use of the CLOEXEC flag in UNIX systems or the HANDLE_FLAG_INHERIT flag on Windows.

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(())
}
codeintel::block_97f1cc82165526a1
fn main()
let listener: TcpListener
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();
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. Panics are meant for unrecoverable errors, and may abort the entire program.

Instead, prefer to use the ? (try) operator, or pattern matching to 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`
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");
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)));
codeintel::block_8fdea0ea42268004
pub fn bind<A>(addr: A) -> Result<TcpListener>
where
    A: ToSocketAddrs,
codeintel::block_8fdea0ea42268004::bind
A
addr: A
core::result
pub enum Result<T, E> {
    Ok( /* … */ ),
    Err( /* … */ ),
}

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

See the module documentation for details.

codeintel::block_b11002248651fbf4
pub trait ToSocketAddrs
codeintel::block_b11002248651fbf4::ToSocketAddrs
pub type Iter: Iterator<Item = SocketAddr>
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.

core::iter::traits::iterator::Iterator
pub type Item

The type of the elements being iterated over.

codeintel::block_b11002248651fbf4::ToSocketAddrs
pub trait ToSocketAddrs
pub fn to_socket_addrs(&self) -> Result<Self::Iter>
self: &Self
codeintel::block_b11002248651fbf4::ToSocketAddrs
Self: ?Sized
#[stable]

Valid forms are:

  • #[stable(feature = “name”, since = “version”)]
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.

codeintel::block_0ad11b6ed467c632
type Iter = vec::IntoIter<SocketAddr>
codeintel::block_0ad11b6ed467c632
pub(in ...) fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>>
self: &str
core::result::Result
Ok(T)

Contains the success value

let addr: {unknown}
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());
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.

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());
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.

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.

Portability

SocketAddr is intended to be a portable representation of socket addresses and is likely not the same as the internal socket address type used by the target operating system’s API. Like all repr(Rust) structs, however, its exact layout remains undefined and should not be relied upon between builds.

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);
codeintel::block_c2d73752298125f8
fn main()
let addr: &str
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 a 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));
codeintel::block_d9826b58817fd798
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.

extern crate libc

libc - Raw FFI bindings to platforms’ system libraries

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,
    /* … */
}
libc::primitives
pub type c_char = i8
libc::primitives
pub type c_int = i32
libc::unix::<extern>
pub unsafe fn freeaddrinfo(res: *mut addrinfo)
libc::unix::linux_like
pub const AF_INET: c_int = 2
libc::unix::linux_like
pub const AF_INET6: c_int = 10 (0xA)
#[no_mangle]

Valid forms are:

  • #[no_mangle]
codeintel::block_0ffaceb5e4ed7d36
unsafe extern "C" fn getaddrinfo(node: *const c_char, service: *const c_char, hints: *const addrinfo, wrapped_rest: *mut *mut addrinfo) -> c_int
node: *const i8
service: *const i8
hints: *const addrinfo
wrapped_rest: *mut *mut addrinfo
let wrapped_ptr: *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_l4re_shared
pub const RTLD_NEXT: *mut c_void = 0xFFFFFFFFFFFFFFFF as *mut c_void
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::{CStr, CString};

// 💀 The meaning of this entire program is undefined,
// 💀 and nothing about its behavior is guaranteed,
// 💀 not even that its behavior resembles the code as written,
// 💀 just because it contains a single instance of undefined behavior!

// 🚨 creates a dangling pointer to a temporary `CString`
// 🚨 that is deallocated at the end of the statement
let ptr = CString::new("Hi!".to_uppercase()).unwrap().as_ptr();

// without undefined behavior, you would expect that `ptr` equals:
dbg!(CStr::from_bytes_with_nul(b"HI!\0").unwrap());

// 🙏 Possibly the program behaved as expected so far,
// 🙏 and this just shows `ptr` is now garbage..., but
// 💀 this violates `CStr::from_ptr`'s safety contract
// 💀 leading to a dereference of a dangling pointer,
// 💀 which is immediate undefined behavior.
// 💀 *BOOM*, you're dead, your entire program has no meaning.
dbg!(unsafe { CStr::from_ptr(ptr) });

This happens because, the pointer returned by as_ptr does not carry any lifetime information, and the CString is deallocated immediately after the expression that it is part of has been evaluated. To fix the problem, bind the CString to a local variable:

use std::ffi::{CStr, CString};

let c_str = CString::new("Hi!".to_uppercase()).unwrap();
let ptr = c_str.as_ptr();

assert_eq!(unsafe { CStr::from_ptr(ptr) }, c"HI!");
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 {
    // Some expensive computation here
    true
}

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::ptr::mut_ptr
impl<T> *mut T
pub const fn is_null(self) -> bool
where
    // Bounds from impl:
    T: PointeeSized,

Examples

let mut s = [1, 2, 3];
let ptr: *mut u32 = s.as_mut_ptr();
assert!(!ptr.is_null());
let mut rest: *mut addrinfo
core
pub mod ptr

Manually manage memory through raw pointers.

See also the pointer primitive types.

Safety

Many functions in this module take raw pointers as arguments and read from or write to them. For this to be safe, these pointers must be valid for the given access. Whether a pointer is valid depends on the operation it is used for (read or write), and the extent of the memory that is accessed (i.e., how many bytes are read/written) – it makes no sense to ask “is this pointer valid”; one has to ask “is this pointer valid for a given access”. Most functions use *mut T and *const T to access only a single value, in which case the documentation omits the size and implicitly assumes it to be size_of::<T>() bytes.

The precise rules for validity are not determined yet. The guarantees that are provided at this point are very minimal:

  • For memory accesses of size zero, every pointer is valid, including the nullpointer. The following points are only concerned with non-zero-sized accesses.
  • A null pointer is never valid.
  • For a pointer to be valid, it is necessary, but not always sufficient, that the pointer bedereferenceable. The provenance of the pointer is used to determine which allocationit is derived from; a pointer is dereferenceable if the memory range of the given sizestarting at the pointer is entirely contained within the bounds of that allocation. Notethat in Rust, every (stack-allocated) variable is considered a separate allocation.
  • All accesses performed by functions in this module are non-atomic in the senseof [atomic operations] used to synchronize between threads. This means it isundefined behavior to perform two concurrent accesses to the same location from differentthreads unless both accesses only read from memory. Notice that this explicitlyincludes read_volatile and write_volatile: Volatile accesses cannotbe used for inter-thread synchronization, regardless of whether they are acting onRust memory or not.
  • The result of casting a reference to a pointer is valid for as long as theunderlying allocation is live and no reference (just raw pointers) is used toaccess the same memory. That is, reference and pointer accesses cannot beinterleaved.

These axioms, along with careful use of [offset] for pointer arithmetic, are enough to correctly implement many useful things in unsafe code. Stronger guarantees will be provided eventually, as the aliasing rules are being determined. For more information, see the book as well as the section in the reference devoted to undefined behavior.

We say that a pointer is “dangling” if it is not valid for any non-zero-sized accesses. This means out-of-bounds pointers, pointers to freed memory, null pointers, and pointers created with NonNull::dangling are all dangling.

Alignment

Valid raw pointers as defined above are not necessarily properly aligned (where “proper” alignment is defined by the pointee type, i.e., *const T must be aligned to align_of::<T>()). However, most functions require their arguments to be properly aligned, and will explicitly state this requirement in their documentation. Notable exceptions to this are read_unaligned and write_unaligned.

When a function requires proper alignment, it does so even if the access has size 0, i.e., even if memory is not actually touched. Consider using NonNull::dangling in such cases.

Pointer to reference conversion

When converting a pointer to a reference (e.g. via &*ptr or &mut *ptr), there are several rules that must be followed:

  • The pointer must be properly aligned.

  • It must be non-null.

  • It must be “dereferenceable” in the sense defined above.

  • The pointer must point to a valid value of type T.

  • You must enforce Rust’s aliasing rules. The exact aliasing rules are not decided yet, so we only give a rough overview here. The rules also depend on whether a mutable or a shared reference is being created.

    • When creating a mutable reference, then while this reference exists, the memory it points tomust not get accessed (read or written) through any other pointer or reference not derivedfrom this reference.
    • When creating a shared reference, then while this reference exists, the memory it points tomust not get mutated (except inside UnsafeCell).

If a pointer follows all of these rules, it is said to be convertible to a (mutable or shared) reference.

These rules apply even if the result is unused! (The part about being initialized is not yet fully decided, but until it is, the only safe approach is to ensure that they are indeed initialized.)

An example of the implications of the above rules is that an expression such as unsafe { &*(0 as *const u8) } is Immediate Undefined Behavior.

Allocation

An allocation is a subset of program memory which is addressable from Rust, and within which pointer arithmetic is possible. Examples of allocations include heap allocations, stack-allocated variables, statics, and consts. The safety preconditions of some Rust operations - such as offset and field projections (expr.field) - are defined in terms of the allocations on which they operate.

An allocation has a base address, a size, and a set of memory addresses. It is possible for an allocation to have zero size, but such an allocation will still have a base address. The base address of an allocation is not necessarily unique. While it is currently the case that an allocation always has a set of memory addresses which is fully contiguous (i.e., has no “holes”), there is no guarantee that this will not change in the future.

Allocations must behave like “normal” memory: in particular, reads must not have side-effects, and writes must become visible to other threads using the usual synchronization primitives.

For any allocation with base address, size, and a set of addresses, the following are guaranteed:

  • For all addresses a in addresses, a is in the range base .. (base + size) (note that this requires a < base + size, not a <= base + size)
  • base is not equal to [null()] (i.e., the address with the numericalvalue 0)
  • base + size <= usize::MAX
  • size <= isize::MAX

As a consequence of these guarantees, given any address a within the set of addresses of an allocation:

  • It is guaranteed that a - base does not overflow isize
  • It is guaranteed that a - base is non-negative
  • It is guaranteed that, given o = a - base (i.e., the offset of a withinthe allocation), base + o will not wrap around the address space (inother words, will not overflow usize)

Provenance

Pointers are not simply an “integer” or “address”. For instance, it’s uncontroversial to say that a Use After Free is clearly Undefined Behavior, even if you “get lucky” and the freed memory gets reallocated before your read/write (in fact this is the worst-case scenario, UAFs would be much less concerning if this didn’t happen!). As another example, consider that [wrapping_offset] is documented to “remember” the allocation that the original pointer points to, even if it is offset far outside the memory range occupied by that allocation. To rationalize claims like this, pointers need to somehow be more than just their addresses: they must have provenance.

A pointer value in Rust semantically contains the following information:

  • The address it points to, which can be represented by a usize.
  • The provenance it has, defining the memory it has permission to access. Provenance can beabsent, in which case the pointer does not have permission to access any memory.

The exact structure of provenance is not yet specified, but the permission defined by a pointer’s provenance have a spatial component, a temporal component, and a mutability component:

  • Spatial: The set of memory addresses that the pointer is allowed to access.
  • Temporal: The timespan during which the pointer is allowed to access those memory addresses.
  • Mutability: Whether the pointer may only access the memory for reads, or also access it forwrites. Note that this can interact with the other components, e.g. a pointer might permitmutation only for a subset of addresses, or only for a subset of its maximal timespan.

When an allocation is created, it has a unique Original Pointer. For alloc APIs this is literally the pointer the call returns, and for local variables and statics, this is the name of the variable/static. (This is mildly overloading the term “pointer” for the sake of brevity/exposition.)

The Original Pointer for an allocation has provenance that constrains the spatial permissions of this pointer to the memory range of the allocation, and the temporal permissions to the lifetime of the allocation. Provenance is implicitly inherited by all pointers transitively derived from the Original Pointer through operations like [offset], borrowing, and pointer casts. Some operations may shrink the permissions of the derived provenance, limiting how much memory it can access or how long it’s valid for (i.e. borrowing a subfield and subslicing can shrink the spatial component of provenance, and all borrowing can shrink the temporal component of provenance). However, no operation can ever grow the permissions of the derived provenance: even if you “know” there is a larger allocation, you can’t derive a pointer with a larger provenance. Similarly, you cannot “recombine” two contiguous provenances back into one (i.e. with a fn merge(&[T], &[T]) -> &[T]).

A reference to a place always has provenance over at least the memory that place occupies. A reference to a slice always has provenance over at least the range that slice describes. Whether and when exactly the provenance of a reference gets “shrunk” to exactly fit the memory it points to is not yet determined.

A shared reference only ever has provenance that permits reading from memory, and never permits writes, except inside [UnsafeCell].

Provenance can affect whether a program has undefined behavior:

  • It is undefined behavior to access memory through a pointer that does not have provenance over that memory. Note that a pointer “at the end” of its provenance is not actually outside its provenance, it just has 0 bytes it can load/store. Zero-sized accesses do not require any provenance since they access an empty range of memory.

  • It is undefined behavior to [offset] a pointer across a memory range that is not contained in the allocation it is derived from, or to [offset_from] two pointers not derived from the same allocation. Provenance is used to say what exactly “derived from” even means: the lineage of a pointer is traced back to the Original Pointer it descends from, and that identifies the relevant allocation. In particular, it’s always UB to offset a pointer derived from something that is now deallocated, except if the offset is 0.

But it is still sound to:

  • Create a pointer without provenance from just an address (see without_provenance). Such a pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be useful for sentinel values like null or to represent a tagged pointer that will never be dereferenceable. In general, it is always sound for an integer to pretend to be a pointer “for fun” as long as you don’t use operations on it which require it to be valid (non-zero-sized offset, read, write, etc).

  • Forge an allocation of size zero at any sufficiently aligned non-null address. i.e. the usual “ZSTs are fake, do what you want” rules apply.

  • [wrapping_offset] a pointer outside its provenance. This includes pointers which have “no” provenance. In particular, this makes it sound to do pointer tagging tricks.

  • Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses are just integers, so there is always a coherent answer, even if the pointers are dangling or from different provenances. Note that if you get “lucky” and notice that a pointer at the end of one allocation is the “same” address as the start of another allocation, anything you do with that fact is probably going to be gibberish. The scope of that gibberish is kept under control by the fact that the two pointers still aren’t allowed to access the other’s allocation (bytes), because they still have different provenance.

Note that the full definition of provenance in Rust is not decided yet, as this interacts with the as-yet undecided aliasing rules.

Pointers Vs Integers

From this discussion, it becomes very clear that a usize cannot accurately represent a pointer, and converting from a pointer to a usize is generally an operation which only extracts the address. Converting this address back into pointer requires somehow answering the question: which provenance should the resulting pointer have?

Rust provides two ways of dealing with this situation: Strict Provenance and Exposed Provenance.

Note that a pointer can represent a usize (via without_provenance), so the right type to use in situations where a value is “sometimes a pointer and sometimes a bare usize” is a pointer type.

Strict Provenance

“Strict Provenance” refers to a set of APIs designed to make working with provenance more explicit. They are intended as substitutes for casting a pointer to an integer and back.

Entirely avoiding integer-to-pointer casts successfully side-steps the inherent ambiguity of that operation. This benefits compiler optimizations, and it is pretty much a requirement for using tools like Miri and architectures like CHERI that aim to detect and diagnose pointer misuse.

The key insight to making programming without integer-to-pointer casts at all viable is the [with_addr] method:

    /// Creates a new pointer with the given address.
    ///
    /// This performs the same operation as an `addr as ptr` cast, but copies
    /// the *provenance* of `self` to the new pointer.
    /// This allows us to dynamically preserve and propagate this important
    /// information in a way that is otherwise impossible with a unary cast.
    ///
    /// This is equivalent to using `wrapping_offset` to offset `self` to the
    /// given address, and therefore has all the same capabilities and restrictions.
    pub fn with_addr(self, addr: usize) -> Self;

So you’re still able to drop down to the address representation and do whatever clever bit tricks you want as long as you’re able to keep around a pointer into the allocation you care about that can “reconstitute” the provenance. Usually this is very easy, because you only are taking a pointer, messing with the address, and then immediately converting back to a pointer. To make this use case more ergonomic, we provide the [map_addr] method.

To help make it clear that code is “following” Strict Provenance semantics, we also provide an [addr] method which promises that the returned address is not part of a pointer-integer-pointer roundtrip. In the future we may provide a lint for pointer<->integer casts to help you audit if your code conforms to strict provenance.

Using Strict Provenance

Most code needs no changes to conform to strict provenance, as the only really concerning operation is casts from usize to a pointer. For code which does cast a usize to a pointer, the scope of the change depends on exactly what you’re doing.

In general, you just need to make sure that if you want to convert a usize address to a pointer and then use that pointer to read/write memory, you need to keep around a pointer that has sufficient provenance to perform that read/write itself. In this way all of your casts from an address to a pointer are essentially just applying offsets/indexing.

This is generally trivial to do for simple cases like tagged pointers as long as you represent the tagged pointer as an actual pointer and not a usize. For instance:

unsafe {
    // A flag we want to pack into our pointer
    static HAS_DATA: usize = 0x1;
    static FLAG_MASK: usize = !HAS_DATA;

    // Our value, which must have enough alignment to have spare least-significant-bits.
    let my_precious_data: u32 = 17;
    assert!(align_of::<u32>() > 1);

    // Create a tagged pointer
    let ptr = &my_precious_data as *const u32;
    let tagged = ptr.map_addr(|addr| addr | HAS_DATA);

    // Check the flag:
    if tagged.addr() & HAS_DATA != 0 {
        // Untag and read the pointer
        let data = *tagged.map_addr(|addr| addr & FLAG_MASK);
        assert_eq!(data, 17);
    } else {
        unreachable!()
    }
}

(Yes, if you’ve been using [AtomicUsize] for pointers in concurrent datastructures, you should be using [AtomicPtr] instead. If that messes up the way you atomically manipulate pointers, we would like to know why, and what needs to be done to fix it.)

Situations where a valid pointer must be created from just an address, such as baremetal code accessing a memory-mapped interface at a fixed address, cannot currently be handled with strict provenance APIs and should use exposed provenance.

Exposed Provenance

As discussed above, integer-to-pointer casts are not possible with Strict Provenance APIs. This is by design: the goal of Strict Provenance is to provide a clear specification that we are confident can be formalized unambiguously and can be subject to precise formal reasoning. Integer-to-pointer casts do not (currently) have such a clear specification.

However, there exist situations where integer-to-pointer casts cannot be avoided, or where avoiding them would require major refactoring. Legacy platform APIs also regularly assume that usize can capture all the information that makes up a pointer. Bare-metal platforms can also require the synthesis of a pointer “out of thin air” without anywhere to obtain proper provenance from.

Rust’s model for dealing with integer-to-pointer casts is called Exposed Provenance. However, the semantics of Exposed Provenance are on much less solid footing than Strict Provenance, and at this point it is not yet clear whether a satisfying unambiguous semantics can be defined for Exposed Provenance. (If that sounds bad, be reassured that other popular languages that provide integer-to-pointer casts are not faring any better.) Furthermore, Exposed Provenance will not work (well) with tools like Miri and CHERI.

Exposed Provenance is provided by the [expose_provenance] and [with_exposed_provenance] methods, which are equivalent to as casts between pointers and integers.

  • [expose_provenance] is a lot like [addr], but additionally adds the provenance of thepointer to a global list of ‘exposed’ provenances. (This list is purely conceptual, it existsfor the purpose of specifying Rust but is not materialized in actual executions, except intools like Miri.)Memory which is outside the control of the Rust abstract machine (MMIO registers, for example)is always considered to be exposed, so long as this memory is disjoint from memory that willbe used by the abstract machine such as the stack, heap, and statics.
  • [with_exposed_provenance] can be used to construct a pointer with one of these previously‘exposed’ provenances. [with_exposed_provenance] takes only addr: usize as arguments, sounlike in [with_addr] there is no indication of what the correct provenance for the returnedpointer is – and that is exactly what makes integer-to-pointer casts so tricky to rigorouslyspecify! The compiler will do its best to pick the right provenance for you, but currently wecannot provide any guarantees about which provenance the resulting pointer will have. Only onething is clear: if there is no previously ‘exposed’ provenance that justifies the way thereturned pointer will be used, the program has undefined behavior.

If at all possible, we encourage code to be ported to Strict Provenance APIs, thus avoiding the need for Exposed Provenance. Maximizing the amount of such code is a major win for avoiding specification complexity and to facilitate adoption of tools like CHERI and Miri that can be a big help in increasing the confidence in (unsafe) Rust code. However, we acknowledge that this is not always possible, and offer Exposed Provenance as a way to explicit “opt out” of the well-defined semantics of Strict Provenance, and “opt in” to the unclear semantics of integer-to-pointer casts.

core::ptr
pub const fn null_mut<T>() -> *mut T
where
    T: PointeeSized + 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
let wrapped_fn: fn(*const i8, *const i8, *const addrinfo, *mut *mut addrinfo) -> i32
core
pub mod mem

Basic functions for dealing with memory.

This module contains functions for querying the size and alignment of types, initializing and manipulating memory.

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 fn() -> i32 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> {
    unsafe { std::mem::transmute::<R<'b>, R<'static>>(r) }
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
                                             -> &'b mut R<'c> {
    unsafe { 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 atype for that buffer, it can use MaybeUninit.
  • If the code actually wants to work on the address the pointer points to, it can use ascasts 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 (ptr, len, capacity) = v_clone.into_raw_parts();
let v_from_raw = unsafe {
    Vec::from_raw_parts(ptr.cast::<*mut Option<&i32>>(), len, 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))
    }
}
let ret: i32
let skip_ipv4: bool
std
pub mod env

Inspection and manipulation of the process’s environment.

This module contains functions to inspect various aspects such as environment variables, process arguments, the current directory, and various other important directories.

There are several functions and structs in this module that have a counterpart ending in os. Those ending in os will return an OsString and those without will return a String.

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.

core::option::Option
impl<T> Option<T>
pub const fn is_some_and(self, f: impl FnOnce(T) -> bool + Destruct) -> 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 x: Option<String> = Some("ownership".to_string());
assert_eq!(x.as_ref().is_some_and(|x| x.len() > 1), true);
println!("still alive {:?}", x);
s: OsString
let skip_ipv6: bool
let mut rest_ptr: *mut *mut addrinfo
let mut current: *mut addrinfo
let family: i32
libc::unix::linux_like::addrinfo
pub ai_family: i32
libc::unix::linux_like::addrinfo
pub ai_next: *mut addrinfo
let to_skip: *mut addrinfo