dead_code warning in Rust when Debug printing a struct - rust

I am very new to Rust and I am wondering about the warning produced by the following code before it runs. I am running version: rustc 1.67.0 (fc594f156 2023-01-24) from the stable channel.
1 #[derive(Debug)]
2 //#[allow(dead_code)]
3 struct Account {
4 balance: f32,
5 }
6
7 fn main() {
8 let account = Account { balance: 10.0 };
9 println!("{:?}", account);
10 }
$ cargo run
Compiling bank v0.1.0 (/Users/rlfeider/projects/rust/bank)
warning: field `balance` is never read
--> src/main.rs:4:5
|
3 | struct Account {
| ------- field in this struct
4 | balance: f32,
| ^^^^^^^
|
= note: `Account` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: `bank` (bin "bank") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `/Users/rlfeider/projects/rust/bank/target/debug/bank`
Account { balance: 10.0 }
$
Why is a warning printed before the code runs?
I would think that the Debug print of the account struct in line 9 would read the balance field.
Does it have something to do with at what point in the compilation of the code that the println! macro is expanded vs when the lint check for dead code occurs?
All things I tried that gets rid of the warning:
Allowing dead_code by uncommenting line 2 removed the warning.
explicitly printing account.balance in the println! also gets rid of the warning.
Making the account variable mutable and setting account.balance to another value before println! also gets rid of the warning.

The error appears before your program is run because it's a compile time warning.
Derived Debug instances are a special case for the dead_code warning, if your code only uses a field in the Debug implementation then it will still consider it unused, i.e. the field isn't used in non debugging code.

Related

Why isn't this out-of-bounds error detected at compile-time?

I've just started coding in rust for a few days now and have stumbled upon this case here that I don't understand
struct Foo {
arr: [u8; 5]
}
fn main() {
let foo = Foo{ arr: [0; 5] };
let bar = &foo;
println!("{}", bar.arr[100]);
}
Why does this code compile? Can't the compiler see that there is an out-of-bounds error there? It can detect it when i try to print foo.arr[100], so what gives?
In general, not all errors that could be detected at compile-time will be reported to the compiler. In this case though, it should be detected since Rust 1.60 and earlier will report this problem, but Rust 1.61 and 1.62 will not:
$ cargo +1.60 build
Compiling mycrate v0.1.0 (/rust-tests)
error: this operation will panic at runtime
--> src/main.rs:133:20
|
133 | println!("{}", bar.arr[100]);
| ^^^^^^^^^^^^ index out of bounds: the length is 5 but the index is 100
|
= note: `#[deny(unconditional_panic)]` on by default
$ cargo +1.61 build
Compiling mycrate v0.1.0 (/rust-tests)
Finished dev [unoptimized + debuginfo] target(s) in 0.78s
This has already been reported as issue #98444: Taking a shared reference of an array suppresses the unconditional_panic lint. You can downgrade your toolchain but hopefully it is resolved soon.

Missing Copy/Debug traits warnings when defining enum with `strum` traits in macro

I want to define an enum using a macro. The enum needs to implement the strum traits {Display, EnumIter, EnumString}. I also want to keep the warnings missing_copy_implementations, missing_debug_implementations on. I came up with the following snippet:
#![warn(
missing_copy_implementations,
missing_debug_implementations,
)]
macro_rules! define_fruits {
{$($fruit:ident -> $name:literal),* $(,)?} => {
#[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
pub enum Fruits {
$(
#[strum(to_string = $name)]
$fruit,
)*
}
};
}
define_fruits! {
Apple -> "green",
Orange -> "orange",
}
The above works fine except I get the warnings:
|
4 | missing_copy_implementations,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `define_fruits` (in Nightly builds, run with -Z macro-backtrace for more info)
and similar for missing_debug_implementations.
These warnings go away when I remove Display, EnumIter, EnumString from my enum. They also go away if I define the same enum outside of a macro.
I cannot seem to find a way to get rid of the warnings in the above scenario, can someone help?
TL/DR: the problem is with derive(EnumIter), which isn't compatible with these lints. I've opened the issue on strum repository asking for possible changes.
To see what really happens, let's try to cargo expand the current code. After some simplification and stubbing out the unnecessary parts, we can see the following structure:
///An iterator over the variants of [Self]
pub struct FruitsIter {
idx: usize,
back_idx: usize,
marker: PhantomData<()>,
}
This is a type you get when you call IntoEnumIterator::iter derived for your enum. There's no Copy or Debug implementation for this struct - they are neither derived nor explicitly added by strum, so the lint sees the violation and fires.
A hint on the problem source can be seen when looking at the exact error, as provided by cargo check (or at the eror highlighting from rust-analyzer):
warning: type could implement `Copy`; consider adding `impl Copy`
--> src/lib.rs:10:27
|
10 | #[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
| ___________________________^
11 | | pub enum Fruits {
| |___________^
Note that the span of the "erroneous" code starts from the EnumIter - since that's the token which the FruitsIter's span is tied to, warning on the FruitsIter is shown as a warning on the derive(EnumIter). And indeed, if we drop this derive - warning disappears.
There's nothing you can do with this, I'm afraid, aside from explicitly allowing these lints for the whole module containing your enum. This is something that should probably be fixed by the strum maintainers.

Cargo build --release option ignored when testing overflow

I've been stepping through the Programming Rust book and wanted to observe the two's complement wrapping, so simple code of:
fn main() {
let mut x: u8 = 255;
println!("the value of x is {}", x) ;
x = 255 + 1 ;
println!("The value of x now is {}",x) ;
}
when I try and compile this with Cargo as per the guide, I run
cargo build --release
which in the book says will let it compile without overflow protection, but it won't compile. I get the protection error
|
6 | x = 255 + 1 ;
| ^^^^^^^^^^^ attempt to compute u8::MAX + 1_u8, which would overflow
Can you explain what I'm doing wrong please ?
I believe the value is not checked dynamically during run-time (it wont panic and would overflow) but still statically checked for (if possible) during compile time.
In this case the compiler is able to determine at compile time what you're trying to do and prevents you from doing it.
That being said if you look at the compiler output you can see the following message:
note: #[deny(arithmetic_overflow)] on by default
You'll see this message regardless of the optimization level.
If you'd like to observe the overflow put the following inner attribute at the top of your source file.
#![allow(arithmetic_overflow)]
Or, if you're compiling with rustc directly you can pass the following flags:
-O -A arithmetic_overflow
The rustc docs show that the following lints are on by default (regardless of optimization level)
ambiguous_associated_items
arithmetic_overflow
conflicting_repr_hints
const_err
ill_formed_attribute_input
incomplete_include
invalid_type_param_default
macro_expanded_macro_exports_accessed_by_absolute_paths
missing_fragment_specifier
mutable_transmutes
no_mangle_const_items
order_dependent_trait_objects
overflowing_literals
patterns_in_fns_without_body
pub_use_of_private_extern_crate
soft_unstable
unconditional_panic
unknown_crate_types
useless_deprecated
When you write a literal 255+1 in your code, the compiler evaluates the expression at compile-time and sees the overflow immediately, whether in debug or release mode. When the book says that --release disables overflow protection, it's talking about runtime checks. You can see the difference with this code:
fn increment (x: u8) -> u8 { x + 1 }
fn main() {
let x = 255;
println!("x: {}, x+1: {}", x, increment (x));
}
Playground
If you run this code in debug mode, you get:
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:1:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
But if you run it in release mode, you get:
x: 255, x+1: 0

Call proc macro inside other proc macro

I have a small reproduction project which fails to compile. The project can be downloaded here: https://github.com/Jasperav/proc_macro_collision. The error is:
error[E0659]: `proc_macro_call` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
I have 3 libraries and 1 executable in the project:
Lib 1 - parser - parses the proc macro call
Lib 2 - proc_two - returns the literal string as a proc macro call
Lib 3 - proc_one - forwards the macro to proc_two (although it does not have a dependency on proc_two). This is like proc_two also a proc macro.
Relevant code for proc_one:
#[proc_macro_hack]
pub fn one(input: TokenStream) -> TokenStream {
let parse = parse_macro_input!(input as Parser);
let r = parse.lit;
let x = quote! {
two!(#r) // This is the problem I guess...
};
x.into()
}
Executable: calls proc_one (gives compile error).
Relevant code:
use proc_macro_hack::proc_macro_hack;
extern crate proc_one;
extern crate proc_two;
#[proc_macro_hack]
use proc_one::one;
#[proc_macro_hack]
use proc_two::two;
fn main() {
let hi: &'static str = one!("hi");
assert_eq!("hi", hi);
}
I don't understand why the call in the executable is ambiguous, lib 2 and 3 do not dependent on each other. This is the full error:
error[E0659]: `proc_macro_call` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
--> src\main.rs:10:1
|
10 | #[proc_macro_hack]
| ^^^^^^^^^^^^^^^^^^ ambiguous name
...
14 | let hi: &'static str = one!("hi");
| ---------- in this macro invocation
|
note: `proc_macro_call` could refer to the macro defined here
--> src\main.rs:11:15
|
11 | use proc_two::two;
| ^^^
...
14 | let hi: &'static str = one!("hi");
| ---------- in this macro invocation
note: `proc_macro_call` could also refer to the macro defined here
--> src\main.rs:9:15
|
9 | use proc_one::one;
| ^^^
...
14 | let hi: &'static str = one!("hi");
| ---------- in this macro invocation
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
According to proc_macro_hack documentation nested invocations are not supported:
By default, nested invocations are not supported i.e. the code emitted by a proc-macro-hack macro invocation cannot contain recursive calls to the same proc-macro-hack macro nor calls to any other proc-macro-hack macros. Use proc-macro-nested if you require support for nested invocations.
Therefore the code marked by you is the real issue:
let x = quote! {
two!(#r) // This is the problem
};
And there is a suggestion to look at proc-macro-nested "if you require support for nested invocations".

Use of Rust alignment feature (issue 33626)

RFC 1358 suggested an alignment attribute #[repr(align="N")] and it was accepted. Rust issue 33626 incorporated the feature into the nightly version.
I'm unable to use this feature with rustc 1.19.0-nightly (777ee2079 2017-05-01). If I compile without the feature gate (#![feature(repr_align)]):
#[repr(align="16")]
struct Foo {
bar: u32,
}
I get the following error statement:
error: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33626)
--> foo.rs:3:1
|
3 | / struct Foo {
4 | | bar: u32,
5 | | }
| |_^
|
= help: add #![feature(repr_align)] to the crate attributes to enable
When I compile with the feature gate, the error message says:
error[E0552]: unrecognized representation hint
--> foo.rs:3:8
|
3 | #[repr(align="16")]
| ^^^^^^^^^^
I also tried the version suggested by the first error message (even though it does not comply with the issue), but still without success. What is the correct way to use the alignment feature?
You can make that feature work when combined with
attribute literals (Playground):
#![feature(repr_align)]
#![feature(attr_literals)]
#[repr(align(16))]
struct Foo {
bar: u32,
}
This is known to work in the latest development version (PR #41673). Searching "repr align" in the Rust compiler's codebase,
all occurrences rely on attribute literals, so it seems likely that the documented form repr(align="N") is not yet supported.

Resources