Skip to main content

intern_lang/
lib.rs

1//! # intern_lang
2//!
3//! Fast string and symbol interning — identifier comparisons become integer
4//! comparisons.
5//!
6//! `intern-lang` maps each distinct string to a small, copyable [`Symbol`],
7//! stores the bytes once in a contiguous backing store, and hands back integer
8//! handles. A compiler front-end interns every identifier once, then compares and
9//! passes symbols instead of strings: a name comparison becomes an integer
10//! comparison, and a name in an AST node costs four bytes instead of an owned
11//! `String`. It is the deduplication layer beneath a lexer and a symbol table,
12//! and it owns interning and nothing else — no lexing, no scoping, no I/O.
13//!
14//! ## At a glance
15//!
16//! - [`Interner`] — the single-threaded interner. [`intern`](Interner::intern)
17//!   deduplicates and returns a stable [`Symbol`];
18//!   [`resolve`](Interner::resolve) borrows the bytes back out of the store.
19//! - [`Symbol`] — a four-byte `Copy` handle whose equality, ordering, and hashing
20//!   are integer operations.
21//! - [`ConcurrentInterner`] — a thread-safe interner many threads can intern into
22//!   at once, sharing one symbol space (requires the `std` feature).
23//! - [`Lookup`] — the read-side trait both interners implement, so generic code
24//!   can accept either.
25//!
26//! ## Example
27//!
28//! ```
29//! use intern_lang::Interner;
30//!
31//! let mut interner = Interner::new();
32//!
33//! // Interning the same string twice yields the same symbol...
34//! let a = interner.intern("while");
35//! let b = interner.intern("while");
36//! assert_eq!(a, b);
37//!
38//! // ...and distinct strings yield distinct symbols.
39//! let c = interner.intern("for");
40//! assert_ne!(a, c);
41//!
42//! // Comparing names is now an integer compare; resolving borrows the bytes.
43//! assert_eq!(interner.resolve(a), Some("while"));
44//! assert_eq!(interner.resolve(c), Some("for"));
45//! ```
46//!
47//! ## `no_std`
48//!
49//! The crate is `no_std`-compatible: it relies only on `alloc`, never on the
50//! standard library. The default `std` feature is additive. Disable default
51//! features to build for a `no_std` target.
52
53#![cfg_attr(not(feature = "std"), no_std)]
54#![cfg_attr(docsrs, feature(doc_cfg))]
55#![forbid(unsafe_code)]
56#![deny(missing_docs)]
57#![deny(unused_must_use)]
58#![deny(unused_results)]
59#![deny(clippy::unwrap_used)]
60#![deny(clippy::expect_used)]
61#![deny(clippy::todo)]
62#![deny(clippy::unimplemented)]
63#![deny(clippy::print_stdout)]
64#![deny(clippy::print_stderr)]
65#![deny(clippy::dbg_macro)]
66#![deny(clippy::unreachable)]
67
68extern crate alloc;
69
70#[cfg(feature = "std")]
71mod concurrent;
72mod interner;
73mod lookup;
74mod symbol;
75
76#[cfg(feature = "std")]
77pub use concurrent::ConcurrentInterner;
78pub use interner::Interner;
79pub use lookup::Lookup;
80pub use symbol::Symbol;