Rust's Prelude
A prelude is the collection of types, traits, functions, and macros that Rust automatically imports into every module of every crate without requiring an explicit use declaration. Because the standard library prelude (std::prelude) is always in scope, you can write Option<T>, Vec<T>, String, Box<T>, or println!(...) directly in any .rs file.
Why Rust Has a Prelude
Even the most basic Rust program relies on standard library items such as Option, Result, Vec, String, Drop, Clone, and Iterator. Without a prelude, every single source file would have to start with a dozen boilerplate use statements:
// Without the prelude, every file would need lines like these:
use std::boxed::Box;
use std::clone::Clone;
use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
use std::convert::{From, Into, TryFrom, TryInto};
use std::default::Default;
use std::iter::{Extend, IntoIterator, Iterator};
use std::marker::{Copy, Send, Sized, Sync};
use std::mem::drop;
use std::ops::{Drop, Fn, FnMut, FnOnce};
use std::option::Option::{self, None, Some};
use std::result::Result::{self, Err, Ok};
use std::string::{String, ToString};
use std::vec::Vec;
Instead, rustc automatically inserts a single hidden import at the top of every module:
use std::prelude::rust_2024::*;
Contents of the Standard Library Prelude (Rust 2024 Edition)
The items included in std::prelude::rust_2024 fall into five categories:
| Category | Automatically Imported Items |
|---|---|
| Core Types & Structs | Box<T>, String, Vec<T> |
| Core Enums & Variants | Option<T> (Some, None), Result<T, E> (Ok, Err) |
| Marker & Memory Traits | Copy, Clone, Sized, Send, Sync, Unpin, Drop, Default |
| Comparison & Conversion Traits | PartialEq, Eq, PartialOrd, Ord, AsRef, AsMut, From, Into, TryFrom, TryInto, FromIterator, ToOwned, ToString |
| Iteration, Closure & Async Traits | Iterator, Extend, IntoIterator, DoubleEndedIterator, ExactSizeIterator, Fn, FnMut, FnOnce, Future, IntoFuture |
| Functions | drop (std::mem::drop) |
| Derive & Built-In Macros | println!, print!, eprintln!, eprint!, format!, vec!, panic!, assert!, assert_eq!, assert_ne!, debug_assert!, todo!, unimplemented!, unreachable!, matches!, dbg!, cfg!, env!, include_str! |
fn main() {
// Every type, variant, trait method, function, and macro below comes from the prelude:
let mut items: Vec<String> = Vec::new();
items.push(String::from("hello"));
let first: Option<&String> = items.iter().next();
let check: Result<u8, _> = 42_u16.try_into();
println!("first = {first:?}, check = {check:?}");
drop(items);
}
first = Some("hello"), check = Ok(42)
How the Prelude Evolved Across Rust Editions
Each Rust Edition can add new items to its edition-specific prelude module (std::prelude::rust_2015, rust_2018, rust_2021, rust_2024) without breaking crates that remain on older editions:
| Edition | Prelude Module | Items Added to the Prelude |
|---|---|---|
| 2015 / 2018 | std::prelude::rust_2015 / rust_2018 |
Box, Option, Result, String, Vec, Copy, Clone, Iterator, From, Into, drop, etc. |
| 2021 | std::prelude::rust_2021 |
TryFrom, TryInto, FromIterator |
| 2024 | std::prelude::rust_2024 |
Future (std::future::Future), IntoFuture (std::future::IntoFuture) |
Because TryFrom and TryInto were added in the 2021 prelude, and Future and IntoFuture were added in the 2024 prelude, modern Rust 2024 code can use .try_into() and impl Future<Output = T> directly without importing std::convert::TryInto or std::future::Future:
use std::pin::Pin;
// In the 2024 edition, `Future` is in the prelude—no `use std::future::Future;` needed!
fn make_ready_future() -> impl Future<Output = i32> {
async { 42 }
}
fn main() {
let small: Result<u8, _> = 100_u32.try_into(); // `TryInto` is also in the prelude
let _fut: Pin<Box<dyn Future<Output = i32>>> = Box::pin(make_ready_future());
println!("Converted: {small:?}");
}
Converted: Ok(100)
Prelude Shadowing Priority
Prelude imports have the lowest name-resolution priority in a module. If you define your own type, function, or use import with the same name as a prelude item (for example, defining a custom type Result<T> = std::result::Result<T, MyError>; or a custom struct Error), your local definition cleanly overrides the prelude item without causing a duplicate-name conflict:
// Define a module-specific `Result` alias that shadows `std::result::Result` from the prelude:
type Result<T> = std::result::Result<T, &'static str>;
fn validate(n: i32) -> Result<i32> {
if n >= 0 {
Ok(n) // `Ok` and `Err` from the prelude still work!
} else {
Err("negative number")
}
}
fn main() {
println!("{:?}", validate(10));
}
Ok(10)
Other Preludes in the Rust Ecosystem
Beyond the main standard library prelude, you will encounter three other kinds of preludes:
core::prelude(#![no_std]): When building bare-metal kernels or embedded firmware with#![no_std], Rust automatically importscore::prelude::rust_2024::*instead ofstd::prelude. It includesOption,Result,Copy,Clone,Iterator, andFuture, omitting only heap-allocating types (Vec,String,Box).std::io::prelude::*: Submodule preludes in the standard library that group commonly needed traits (Read,Write,BufRead,Seek) for a singleuse std::io::prelude::*;import.- External Crate Preludes (
use rayon::prelude::*;): Libraries that rely heavily on extension traits often provide apreludemodule so users can import all key traits in one line.