What would a minimal std look like?

Imagine Rust's standard library could be shrunk down to its bare essentials; it contains only that which can't be implemented by third party crates using the stable compiler.

What would that look like? I believe it would have at least these modules:

  • mem
  • ptr
  • num (for NonZero types)
  • hint

And also UnsafeCell. But I think I'm missing a lot of other essentials.

1 Like

Rust provides a core crate and an alloc crate.

1 Like

Sure, but can't much of that be implemented by third party crates? For example time doesn't do anything special, no? And for that matter is alloc doing anything that a crate couldn't do?

I mean there are many types that are integrated into the compiler, e.g. the IntoIterator trait is linked to for loops, even if you could otherwise implement it yourself.

Anything that's a #[lang] item is closely tied to the compiler, like Box.

core::fmt is tightly integrated with format_args!. Box allows moving the inner value out (let a: Vec<u8> = *Box::new(vec![0]); is allowed, notice the *) alloc also adds some methods to str, which is normally not allowed as str is not a type defined inside alloc. Everything in core::marker is special cased by the compiler. core::arch is mostly implemented using LLVM intrinsics that are not accessible on stable. core::future is used by async fn.

1 Like

core::result and core::option must be in the standard library so that other standard library APIs can use them. For example, Option is needed by the Iterator trait. (Also, they need to implement the unstable Try trait to support the ? operator, and unstable Termination trait to support fn main() -> Result.)

The core::ops traits are all lang items to support operator overloading.

core::any relies on the unstable type_id intrinsic.

core::pin::Pin is a lang item. (Is this needed for async/await? I'm not sure.)

core::clone::Clone is a lang item so that the Copy trait can depend on it. Removing that relationship now would be a breaking change, though if it had been removed pre-1.0, I don't think it would cause any fundamental problems.

core::slice::from_raw_parts used to require unstable code to implement, but in Rust 1.42 and later it can be implemented in terms of core::ptr::slice_from_raw_parts.

core::sync::atomic is implemented using unstable intrinsics.

At least some of core::poll is needed for async/await.

Some core macros like file! and line! and include! require compiler support.

libcore also contains inherent methods on built-in types, like u32::count_ones and str::as_bytes, which use intrinsics or other unstable code.

1 Like

Quite the opposite, it might be one of the more complex interfaces in terms of necessary maintenance. The core crate has those parts of time that are fully standalone and is hence very simple but also small: It has only a Duration structure. This is because std::time::Instant needs to interact with the clock source of the operating system, which means dealing with (non-)monotonicity, epochs, clock resolution. If you think time is simple, think again. Perhaps the easiest why to explain that it is hard is because time is not something Rust can influence or define like a memory model, it needs to conform to what is available while still providing the minimum for thread, scheduling etc. And the really complex stuff such as timezones is indeed not handled by the standard library but by crates such as chrono.

Additionally, unsizing coercions (e.g. Box<T> to Box<dyn Trait>) require the unstable CoerceUnsized trait, so all types that implement that trait (and any types that depend on those) must be in the standard library until it is stabilized. This includes RefCell, Arc, and Rc.

Some standard slice-like types like Path and OsStr are implemented using unstable assumptions that are not valid outside of the standard library. This might be fixable, though.

I think a lot of the panic-related code (core::panic, std::panic, std::thread::panicking) relies on integration with the startup glue that is not accessible to external crates.

For sure, I didn't mean it wasn't a hard or complicated thing to do. I meant "special" in the sense it doesn't require compiler magic to implement so other crates can do the same job.

I believe that std::time and core::time could all be implemented outside of the standard library, as long as other APIs that depend on them (like std::thread::sleep and Condvar::wait_timeout) were also moved to external crates.

std::env::args is integrated with the compiler-generated startup code, and could not easily be moved to an external crate. On at least some platforms, it could be implemented in some other way outside of libstd.