Questions about rust lints

For my kernel and its libk library I've turned on various lints; specifically, the following:

For the binary:

#![forbid(
    absolute_paths_not_starting_with_crate,
    anonymous_parameters,
    deprecated_in_future,
    explicit_outlives_requirements,
    indirect_structural_match,
    keyword_idents,
    macro_use_extern_crate,
    meta_variable_misuse,
    missing_abi,
    missing_copy_implementations,
    missing_debug_implementations,
    non_ascii_idents,
    noop_method_call,
    pointer_structural_match,
    private_doc_tests,
    rust_2021_incompatible_closure_captures,
    rust_2021_incompatible_or_patterns,
    rust_2021_prefixes_incompatible_syntax,
    rust_2021_prelude_collisions,
    semicolon_in_expressions_from_macros,
    single_use_lifetimes,
    trivial_casts,
    trivial_numeric_casts,
    unaligned_references,
    unreachable_pub,
    unsafe_op_in_unsafe_fn,
    unused_crate_dependencies,
    unused_import_braces,
    unused_lifetimes,
    unused_qualifications,
    variant_size_differences,
    warnings,
    box_pointers
)]
#![forbid(clippy::all)]

And for the library:

#![forbid(
    absolute_paths_not_starting_with_crate,
    anonymous_parameters,
    deprecated_in_future,
    explicit_outlives_requirements,
    indirect_structural_match,
    keyword_idents,
    macro_use_extern_crate,
    meta_variable_misuse,
    missing_abi,
    missing_docs,
    non_ascii_idents,
    noop_method_call,
    pointer_structural_match,
    private_doc_tests,
    rust_2021_incompatible_closure_captures,
    rust_2021_incompatible_or_patterns,
    rust_2021_prefixes_incompatible_syntax,
    rust_2021_prelude_collisions,
    semicolon_in_expressions_from_macros,
    single_use_lifetimes,
    trivial_numeric_casts,
    unaligned_references,
    unused_crate_dependencies,
    unused_extern_crates,
    unused_import_braces,
    unused_lifetimes,
    variant_size_differences,
    warnings,
            trivial_casts
)]
#![deny(
    missing_copy_implementations,
    missing_debug_implementations,
)]
#![forbid(clippy::all)]

I was going to add unreachable_pub but that threw errors and was wanting me to make a ton of things pub(crate) that were originally pub, and I was going to add unsafe_op_in_unsafe_fn but that was a bit of an antithetical lint because it wanted me to add unsafe blocks in unsafe functions (which didn't make sense and would've raised more errors). So my questions are:

  1. Is it a good idea to enable these lints?
  2. Is a better description for each lint provided somewhere else? The rustc help doesn't really elaborate on what the lints do. An example is the unused_qualifications lint -- in the past that simply printed a useless error message of "error: unused qualification". Its a bit more helpful now but the output of rustc -W help is pretty useless for some of these.
  3. Why do some of these lints exist? What are good situations that they would apply to?
  4. Could these lints be more of a problem than a help?
  5. Why aren't some of these lints (e.g. unused_crate_dependencies) enabled by default? Some of them seem like no-brainers to me.

I found this link, which provides a description and rational for many of the lints.

https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unused-qualifications

I imagine some of the lints you mentioned would be considered "hurtful" in typical rust code, e.g. forbid(box_pointers), which would cause compilation to fail if you use a Box anywhere. Most rust programs appreciate being able to use this core language feature. But, there might be special cases where you would want to deny access to it for a codebase.

1 Like

When you're writing an unsafe fn, you will be performing at least one unsafe operation in the function body (otherwise the function couldn't ever cause UB and wouldn't need to be marked unsafe). But it's likely that you'll also be performing many completely safe operations as well, so it can be useful to delimit the parts of the body where you know you're doing unsafe stuff, so that human readers can find those parts easily and the compiler can catch many unintentional unsafe operations. This is what unsafe_op_in_unsafe_fn allows you to do. Without it, the whole function body is effectively inside an unsafe block, so you can't distinguish the parts that are supposed to be safe.

3 Likes

Thank you for those explanations. These lints are nice-haves. Even if they can be overly pedantic and annoying sometimes. According to the Rustc book some lints aren't fully stable yet but that's okay.

I am personally a huge fan of unreachable_pub. It's the only non-standard lint I enable.

The reason I like it is that it makes the pub keyword significantly more informative. If this lint is enabled, I know that everything marked as pub is, in fact, crate's public API, so I need to be extra careful about this.

Without this lint, pub items might not actually be exported from the crate root, so the "is this my public API" question becomes non-local.

I agree. Problem is that one of my proc macros generates normal "pub" items, and I don't think it'll allow me to restrict their visibility. Might switch to another one because it also generates #[allow(unreachable_code)] and I have that as forbid. But rust treats it as a warning for now but it'll be a hard error soon enough.

That's a reason to have a lint as #![deny] and not #![forbid]. Personally, I use #![forbid] only for unsafe_code (where I don't have unsafe code), or unsafe_op_in_unsafe_fn (where I do have).

Pedantic note: this is not always the case. Sometimes you mark one function as unsafe, even though it does not perform any unsafe operations, so that other functions in the module will be safe.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.