| Crates.io | prudent |
| lib.rs | prudent |
| version | 0.0.3-beta |
| created_at | 2025-10-24 09:50:19.066686+00 |
| updated_at | 2025-11-27 04:09:55.105946+00 |
| description | Help you make Rust code safer. For both authors and reviewers. |
| homepage | https://github.com/prudent-rs/prudent |
| repository | https://github.com/prudent-rs/prudent |
| max_upload_size | |
| id | 1898228 |
| size | 99,571 |
prudent helps you minimize the amount of Rust code that is marked as unsafe.
Results of prudent's macro invocations are const (if the original invocation/expression would
also be const).
Because of some Rust annoyances (more below), a part of this crate needs to be "loaded". (That is
not at runtime/dynamic, but it's done at compile time.) You do it only once per your crate
(usually in src/lib.rs):
prudent's file src/linted.rs, which
you "load" with ::prudent::load!(...).::prudent::load().Both ways of ::prudent::load!(...) create a module, called prudent by default. If your crate
already uses prudent identifier, you can choose a different identifier for prudent's top-level
module (by passing an optional "parameter" to ::prudent::load!(...)).
Have a wildcard import use crate::prudent::*. Do not import just a specific "top level" (client
code-facing) macro(s) that you invoke. That is regardless of whether you apply the lints (where your
include src/linted.rs), or not.
(At the top level of your crate you could use self::prudent::* instead, but that will not work
in modules. However, use crate::prudent::* works everywhere).
prudent is badly affected by lack of lint control in macros:
rust-lang/rust#110613 - please give it thumbs up.
The pains (that pend rust-lang/rust#110613):
prudent's documentation on docs.rs shows code
examples first, and only then documentation text (prose).
You need a wildcard import use crate::prudent::* - not just import a specific "top level"
(client code-facing) macro(s) that you invoke.
It's not enough to import just specific macros that you invoke (because the internal "linted"
macros are loaded in your crate's namespace, and hence they can't use $crate metavariable to
refer to the rest of the macros and non-macro functionality).
In doctests
any: like ::prudent::load!(any: "linted.rs");use crate::prudent::*; which you put outside of your fn main()fn main()
fn main(), which will include
::prudent::load!(...) and use crate::prudent::*, which will fail with very strange errors.
Even if all you are testing is const, have an empty fn main() {}. (If you run cargo clippy and it complains, see prudent's source code on how to allow
clippy::needless_doctest_main.)Macro unsafe_method (normally accessed as crate::prudent::unsafe_method)
TODO TODO TODO!
Checks and tests are run by GitHub Actions. See
results. All tests run on
Alpine Linux (without libc, in a rust:1.87-alpine container):
rustup component add clippy rustfmtcargo clippycargo fmt --checkcargo doc --no-deps --quietcargo testcargo test --releaseMIRI
rustup install nightly --profile minimalrustup +nightly component add miricargo +nightly miri testcargo +nightly test, (The Rustdoc book > Unstable features > Error numbers for compile-fail
doctests).
The error codes are validated by GitHub Actions, see
results. Error code validation requires nightly
Rust toolchain. See also src/linted_with_tests.rs for expected
compilation error codes.Following are all the positive examples. They are also run by the above GitHub Actions as doctests.
For negative examples, which catch unintended unsafe functions/expressions/access, see
documentation of each prudent macro.
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const unsafe fn unsafe_fn_no_args() {}
const unsafe fn unsafe_fn_one_arg(b: bool) -> bool { b }
const unsafe fn unsafe_fn_two_args(_: bool, u: u8) -> u8 { u }
const _: () = unsafe_fn!(unsafe_fn_no_args);
const _: bool = unsafe_fn!(unsafe_fn_one_arg=> true);
const _: u8 = unsafe_fn!(unsafe_fn_two_args=> true, 0);
fn main() {}
::prudent::load!(any: "linted.rs");
mod module {
use crate::prudent::*;
// Works for Copy types
const _: u8 = unsafe_method!(1u8 =>@ unchecked_add => 0);
//const _: u8 = unsafe_method!(({#[forbid(unused)] let v = 1u8; v}), unchecked_add, 0);
//const _: u8 = unsafe_method!(#[allow_unsafe] 1u8, unchecked_add, 0);
//const _: u8 = unsafe_method!(#[expect_unsafex] 1u8, unchecked_add, 0);
//const _: u8 = unsafe_method!(({#forbid(unused) let v = 1u8; v}), unchecked_add, 0);
const _: u8 = unsafe_method!(~allow_unsafe 1u8 =>@ unchecked_add => 0);
//const _: u8 = unsafe_method!(~expect_unsafe 1u8, unchecked_add, 0);
}
fn main() {}
let _todo = ();
//# use prudent::unsafe_method;
//const _: u8 = unsafe_method!(~expect_unsafe ~allow_unsafe 1u8, unchecked_add, 0);
let _todo = ();
//# use prudent::unsafe_method;
//const _: u8 = unsafe_method!(~allow_unsafe ~expect_unsafe 1u8, unchecked_add, 0);
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
struct SNonCopy {}
impl SNonCopy {
unsafe fn unsafe_method_no_args(&self) {}
unsafe fn unsafe_method_one_arg(&self, _: bool) {}
unsafe fn unsafe_method_two_args(&self, _: bool, _: bool) {}
}
fn main() {
let s = SNonCopy {};
// Works for non-Copy types
unsafe_method!(s =>@ unsafe_method_no_args);
unsafe_method!(s =>@ unsafe_method_one_arg => true);
unsafe_method!(s =>@ unsafe_method_two_args => true, false);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
struct SNonCopy {}
impl SNonCopy {
unsafe fn unsafe_method_no_args(&mut self) {}
unsafe fn unsafe_method_one_arg(&mut self, _: bool) {}
unsafe fn unsafe_method_two_args(&mut self, _: bool, _: bool) {}
}
fn main() {
let mut s = SNonCopy {};
unsafe_method!(s =>@ unsafe_method_no_args);
unsafe_method!(s =>@ unsafe_method_one_arg => true);
unsafe_method!(s =>@ unsafe_method_two_args => true, false);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
{
struct SNonCopy {}
impl SNonCopy {
unsafe fn unsafe_method_no_args(self) {}
unsafe fn unsafe_method_one_arg(self, _: bool) {}
unsafe fn unsafe_method_two_args(self, _: bool, _: bool) {}
}
unsafe_method!(SNonCopy {} =>@ unsafe_method_no_args);
unsafe_method!(SNonCopy {} =>@ unsafe_method_one_arg => true);
unsafe_method!(SNonCopy {} =>@ unsafe_method_two_args => true, false);
}
{
#[derive(Clone, Copy)]
struct SCopy {}
impl SCopy {
unsafe fn unsafe_method_no_args(self) {}
}
let sCopy = SCopy {};
unsafe_method!(sCopy =>@ unsafe_method_no_args);
unsafe_method!(sCopy =>@ unsafe_method_no_args);
let _ = sCopy;
}
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: &bool = unsafe_ref!(PT);
fn main() {
let _ = unsafe_ref!(PT);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const BS: [bool; 2] = [true, false];
const PT: *const [bool] = &BS as *const [bool];
const _: &[bool] = unsafe_ref!(PT);
fn main() {
let _ = unsafe_ref!(PT);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
# use core::fmt::Display;
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: &dyn Display = unsafe_ref!(PT);
fn main() {}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: &'static bool = unsafe_ref!(PT, 'static);
fn main() {
let _ = unsafe_ref!(PT, 'static);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
use core::fmt::Display;
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: &'static dyn Display = unsafe_ref!(PT, 'static);
fn main() {}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const BS: [bool; 2] = [true, false];
const PT: *const [bool] = &BS as *const [bool];
const _: &'static [bool] = unsafe_ref!(PT, 'static);
fn main() {
let _ = unsafe_ref!(PT, 'static);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: &bool = unsafe_ref!(PT, bool);
fn main() {
let _ = unsafe_ref!(PT, bool);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
const BS: [bool; 2] = [true, false];
const PT: *const [bool] = &BS as *const [bool];
const _: &[bool] = unsafe_ref!(PT, [bool]);
fn main() {
let _ = unsafe_ref!(PT, [bool]);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
# use core::fmt::Display;
const B: bool = true;
const PT: *const dyn Display = &B as *const dyn Display;
const _: &dyn Display = unsafe_ref!(PT, dyn Display);
fn main() {
let _ = unsafe_ref!(PT, dyn Display);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let mut b: bool = true;
let pt: *mut bool = &mut b as *mut bool;
let _: &bool = unsafe_mut!(pt);
let _ = unsafe_mut!(pt);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let mut bs: [bool; 2] = [true, false];
let pt: *mut [bool] = &mut bs as *mut [bool];
let _: &[bool] = unsafe_mut!(pt);
let _ = unsafe_mut!(pt);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
# use core::fmt::Display;
fn main() {
let mut b: bool = true;
let pt: *mut bool = &mut b as *mut bool;
let _: &mut dyn Display = unsafe_mut!(pt);
let _: &dyn Display = unsafe_mut!(pt);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let b: &'static mut bool = Box::leak( Box::new(true) );
let pt: *mut bool = b as *mut bool;
let _: &'static mut bool = unsafe_mut!(pt, 'static);
let _ = unsafe_mut!(pt, 'static);
# let _drop_for_miri = unsafe { Box::from_raw(b) };
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
# use core::fmt::Display;
fn main() {
let b: &'static mut bool = Box::leak( Box::new(true) );
let pt: *mut bool = b as *mut bool;
let _: &'static mut dyn Display = unsafe_mut!(pt, 'static);
let _ = unsafe_mut!(pt, 'static);
# let _drop_for_miri = unsafe { Box::from_raw(b) };
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let bs: &'static mut [bool] = Box::leak( Box::new([true, false]) );
let pt: *mut [bool] = bs as *mut [bool];
let _: &'static mut [bool] = unsafe_mut!(pt, 'static);
let _ = unsafe_mut!(pt, 'static);
# let _drop_for_miri = unsafe { Box::from_raw(bs) };
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let mut b: bool = true;
let pt: *mut bool = &mut b as *mut bool;
let _: &mut bool = unsafe_mut!(pt, bool);
let _ = unsafe_mut!(pt, bool);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let bs: &'static mut [bool] = Box::leak( Box::new([true, false]) );
let pt: *mut [bool] = bs as *mut [bool];
let _: &mut [bool] = unsafe_mut!(pt, [bool]);
let _ = unsafe_mut!(pt, [bool]);
# let _drop_for_miri = unsafe { Box::from_raw(bs) };
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
# use core::fmt::Display;
fn main() {
let mut b: bool = true;
let pt: *mut dyn Display = &mut b as *mut dyn Display;
let _: &mut dyn Display = unsafe_mut!(pt, dyn Display);
let _ = unsafe_mut!(pt, dyn Display);
}
Only for types that implement/derive core::marker::Copy.
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: bool = unsafe_val!(PT);
let _ = unsafe_val!(PT);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
const B: bool = true;
const PT: *const bool = &B as *const bool;
const _: bool = unsafe_val!(PT, bool);
let _ = unsafe_val!(PT, bool);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
fn main() {
let mut b: bool = true;
let pt: *mut bool = &mut b as *mut bool;
unsafe_set!(pt, false);
unsafe_set!(pt, true);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
struct SNonCopy {}
fn main() {
let mut s: SNonCopy = SNonCopy {};
let pt: *mut SNonCopy = &mut s as *mut SNonCopy;
let setFrom: SNonCopy = SNonCopy {};
unsafe_set!(pt, setFrom);
let setFrom: SNonCopy = SNonCopy {};
unsafe_set!(pt, setFrom);
}
::prudent::load!(any: "linted.rs");
use crate::prudent::*;
static mut B: bool = true;
fn main() {
unsafe_static_set!(B, false);
}
prudent helps both authors, reviewers and all of us:
unsafe code:
unsafe function or methodunsafe) function (that is to be evaluated)self) of an unsafe methodstatic mut variablesunion typesunsafeunsafe {...} block, andunsafe. (Of course, such a change is a breaking change, but
mistakes happen.)However,
prudent cannot make/guarantee your unsafe code to be "safe". No tool can fully do that
(because of nature of unsafe).prudent doesn't mean you can ignore/skip reviewing/blindly trust any safe code that
calls/interacts with/is used by unsafe code or with data used by both. "Unsafe Rust cannot
trust Safe Rust without care." and
"Unsafe code must trust some Safe code, but shouldn't trust generic Safe
code."prudent is no-std-compatible.
prudent is planned to be always below version 1.0. So
it will be forward compatible. (If a need ever arises for big incompatibility, that can go to a new
crate.)
That allows you to specify prudent as a dependency with version 0.*, which will match ANY
major versions (below 1.0, of course). That will match the newest
This is special only to 0.* - it is not possible to have a wildcard matching various major
versions 1.0 or higher.
Rust is a rich language and it allows complex statements/expressions. prudent tries to be
flexible, but it also needs to be manageable and testable. So, there may be code that prudent
doesn't accept (please do report it). Most likely if it involves advanced pattern matching.
prudent is to help you make unsafe code stand out more. Mixing unsafe with advanced pattern
matching or other complex syntax may sound exciting, but it makes reading the code difficult. Can
that be an opportunity to refactor?
Several prudent macros duplicate their expression "parameter". In the generated Rust code the
parameter expression is evaluated only once, but it's present in the code twice - once in an
inactive if false {...} branch for verification, and once in the following active else {...}
branch.
That is OK with macros by example (defined with macro_rules!), and OK with any well-behaving
procedural macros. However, if you pass in an expression that invokes a procedural macro that has
side effects or state, it's your problem. Such a macro contradicts Rust guidelines.
Please subscribe for low frequency updates at peter-lyons-kehl/prudent#1.
Please contribute, or at least subscribe, and give thumbs up, to:
Sorted by importance (for prudent):
#![doc = include_str!()]--no-run flag in
rustdoc#![feature(const_trait_impl)]${ignore(..._}
metavariable/metafunction in macro_rules!unboxed_closures and
fn_traits feature.#![doc(test(attr(forbid(...))))] for lint groups#![forbid(unsafe_code)] library can emit unsafe