Choose Rust types at compile-time via boolean constants, brought to you by Nikolai Vazquez.
If you find this library useful, consider starring it as well as sponsoring or donating once. 💖
The CondType
type and condval!
macro choose types at compile-time using
bool
constants, just like std::conditional_t
in C++.
Unlike the Either
type, the type chosen by CondType
/condval!
is
directly used, rather than wrapped with an enum
type. This may be considered
a form of dependent typing, but
it is limited in ability and is restricted to compile-time constants rather than
runtime values.
In the following example, CondType
aliases either &str
or i32
,
depending on the boolean generic constant:
use condtype::CondType;
let str: CondType<true, &str, i32> = "hello";
let int: CondType<false, &str, i32> = 42;
// Unsized types are also supported:
let str: &CondType<true, str, [u8]> = "world";
condval!
enables choosing differently-typed values without specifying types.
In the following example, val
is inferred to be either &str
or i32
,
depending on COND
.
use condtype::condval;
const COND: bool = true;
let val = condval!(if COND {
"hello"
} else {
42
});
assert_eq!(val, "hello");
if let
pattern matching is also supported:
use condtype::condval;
const STR: Option<&str> = Some("hello");
let val = condval!(if let Some(str) = STR {
str.to_uppercase()
} else {
42
});
assert_eq!(val, "HELLO");
This library can make code for some platforms more efficient by using smaller-sized types, depending on platform-specific constants.
In the following example, the RlimOption
type can be either
Option<rlim_t>
or rlim_t
itself,
where rlim_t::MAX
can be treated as a sentinel value for
Option::None
if it is not equal to RLIM_INFINITY
.
use condtype::{condval, CondType};
use libc::{rlim_t, RLIM_INFINITY};
const RLIM_INFINITY_IS_MAX: bool = RLIM_INFINITY == rlim_t::MAX;
type RlimOption = CondType<RLIM_INFINITY_IS_MAX, Option<rlim_t>, rlim_t>;
const RLIM_NONE: RlimOption = condval!(if RLIM_INFINITY_IS_MAX {
None::<rlim_t>
} else {
rlim_t::MAX
});
// Convert from either `RlimOption` type to `Option` via the `Into` trait:
let rlim_none: Option<rlim_t> = RLIM_NONE.into();
Without this library, one could otherwise use cfg_if!
to achieve the same
goal. However, using #[cfg]
requires maintaining a list of platforms
and being more fine-grained if RLIM_INFINITY
is dependent on CPU
architecture.
use cfg_if::cfg_if;
use libc::rlim_t;
cfg_if! {
// Platforms where `RLIM_INFINITY != rlim_t::MAX`:
if #[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "solaris",
// ad nauseam...
))] {
type RlimOption = rlim_t;
const RLIM_NONE: RlimOption = rlim_t::MAX;
} else {
type RlimOption = Option<rlim_t>;
const RLIM_NONE: RlimOption = None;
}
}
It is currently not possible to use CondType
or condval!
with a
generic constant because Rust does not yet consider trait
implementations based on booleans to be exhaustive.
Once that issue is resolved, all versions of this library should just work
with generic constants.
fn generic<const B: bool>() {
let val: CondType<B, &str, i32> = condval!(if B {
"hello"
} else {
42
});
}
This library is available on crates.io and
can be used by running the following cargo
command in your project directory:
cargo add condtype
or by manually adding the following to your project's Cargo.toml
:
[dependencies]
condtype = "1.3.0"
Like the Rust project, this library may be used under either the MIT License or Apache License (Version 2.0).