| Crates.io | neobit |
| lib.rs | neobit |
| version | 1.0.4 |
| created_at | 2025-12-28 12:56:07.42299+00 |
| updated_at | 2026-01-06 05:51:08.222042+00 |
| description | Zero-dependency, lightweight bitflags with readable debug output |
| homepage | |
| repository | https://github.com/but212/neobit |
| max_upload_size | |
| id | 2008787 |
| size | 138,336 |
A zero-dependency, bitflags macro for systems programming. Designed for no_std environments.
Bit operations are simple. The library should be too.
neobit provides union, intersection, difference, complement, and membership testing. Nothing more, nothing less.
use neobit::neobit;
neobit! {
pub struct Permissions: u8 {
const READ = 0b001;
const WRITE = 0b010;
const EXECUTE = 0b100;
}
}
fn main() {
let perms = Permissions::READ | Permissions::WRITE;
assert!(perms.contains(Permissions::READ));
assert!(!perms.contains(Permissions::EXECUTE));
println!("{:?}", perms); // Permissions(READ | WRITE)
// From trait - from raw bits
let from_raw: Permissions = 0b111.into();
assert!(from_raw.is_all());
// All defined flags
let all = Permissions::all();
// Bit validation (returns Option)
let valid = Permissions::from_bits(0b011); // Some(...)
let invalid = Permissions::from_bits(0b1000); // None
}
no_std compatible - Works in embedded environmentsu8-u128 and i8-i128Flags(READ | WRITE) instead of Flags { bits: 3 }const fn operations - Use in const contexts!flags inverts all bits, not just known flagsno_std)Composite constants can be defined in the macro, but require using .union().bits() syntax:
neobit! {
pub struct Flags: u8 {
const A = 0b001; // ✅ Single bit
const B = 0b010; // ✅ Single bit
const AB = Self::A.union(Self::B).bits(); // ✅ Composite constant - requires .bits()
}
}
Alternatively, define composite constants outside the macro for cleaner syntax:
impl Flags {
pub const AB: Self = Self::A.union(Self::B); // ✅ Cleaner approach
}
// Using From trait (recommended for hardware/FFI)
let flags_into: Flags = 0x1234.into(); // Preserves all bits
let flags_from = Flags::from(0x1234); // Same as above
// Other construction methods
Flags::empty() // No flags set
Flags::all() // All defined flags
Flags::from_bits(bits) // Validated, returns Option<Self>
Flags::from_bits_truncate(bits) // Truncate unknown bits
Flags::from_bits_retain(bits) // Keep all bits (same as From)
flags.contains(other) // All bits in other are in flags
flags.intersects(other) // Any bits in other are in flags
flags.is_empty() // No bits set
flags.is_all() // All defined flags set
flags.bits() // Raw bit value
flags.insert(other) // Add flags
flags.remove(other) // Remove flags
flags.toggle(other) // Flip flags
flags.set(other, condition) // Set or remove based on bool
| Operator | Meaning | const fn equivalent |
|---|---|---|
| |
Union | union() |
& |
Intersection | intersection() |
^ |
Symmetric difference | symmetric_difference() |
! |
Complement | complement() |
- |
Difference | difference() |
All operators have *Assign variants (|=, &=, etc.).
const MASK: Flags = Flags::A.union(Flags::B);
const ALL: Flags = Flags::all();
neobit implements From<T> trait for seamless conversion (unlike bitflags which only provides TryFrom):
// From integer to flags (preserves all bits)
let flags: Flags = 0xFF.into(); // Recommended, equivalent to `Flags::from(0xFF)`
// From flags to integer
let bits1: u8 = flags.into();
let bits2 = u8::from(flags);
let bits3 = flags.bits(); // Also works
Note:
Fromtrait usesfrom_bits_retaininternally, preserving all bits including unknown ones. This is intentional for hardware/FFI use cases.
neobit and bitflags implement complement() differently:
neobit! {
pub struct Flags: u8 {
const A = 0b01;
const B = 0b10;
}
}
let flags = Flags::A; // 0b00000001
// neobit: Pure bitwise NOT
let comp = !flags; // 0b11111110
// bitflags: Masked to known flags
// !flags -> 0b00000010
neobit preserves all bit information, which is essential for hardware registers and protocol handling.
Signed integers are supported for C FFI compatibility, but be careful with ! (complement):
neobit! {
pub struct SignedFlags: i8 {
const A = 0b0001;
}
}
let complement = !SignedFlags::A;
// i8: !0b0001 = -2 (two's complement)
// u8: !0b0001 = 254
// Prefer difference() for removing flags:
let all = SignedFlags::all();
let without_a = all.difference(SignedFlags::A);
use neobit::neobit;
// Define flags matching a C header
neobit! {
#[repr(transparent)]
pub struct RegisterFlags: u32 {
const READY = 0x01;
const ERROR = 0x02;
const BUSY = 0x04;
const DATA_RDY = 0x08;
}
}
// Safe Rust wrapper around C functions
fn read_status() -> RegisterFlags {
let raw = read_register();
raw.into() // From trait preserves all bits!
}
fn set_ready_flag() {
let current = read_status();
let updated = current | RegisterFlags::READY;
write_register(updated.bits());
}
// See examples/c_ffi_simple.rs for a complete runnable example
Single-bit flags are shown by name. Composite constants are expanded:
println!("{:?}", Flags::READ); // Flags(READ)
println!("{:?}", Flags::READ | Flags::WRITE); // Flags(READ | WRITE)
println!("{:?}", Flags::all()); // Flags(READ | WRITE | EXECUTE)
println!("{:?}", Flags::empty()); // Flags(empty)
println!("{:?}", Flags::from(0x80)); // Flags(0x80)
Check out the examples/ directory for comprehensive demonstrations:
quick_start.rs - Basic usage with file permissionspermissions.rs - File permission flags exampleall_method.rs - Using the built-in all() methodbit_validation.rs - Safe vs unchecked bit operationsvalidation.rs - Input validation patternsoperators_and_methods.rs - All available operationstype_conversion.rs - Converting between integers and flagsconst_operations.rs - Using flags in const contextsinteger_types.rs - All supported integer types (u8-u128, i8-i128)composite_flags.rs - Defining composite flag constantscomplement_difference.rs - How neobit differs from bitflagslimitations.rs - Macro limitations and workaroundsc_ffi.rs - Complete C FFI integration examplec_ffi_simple.rs - Simple C FFI and hardware register examplehardware_register.rs - Hardware register manipulationnetwork_protocol.rs - Network protocol flagsRun them with:
cargo run --example <example_name>
Rust 1.56 or later.
Licensed under either of:
at your option.