@@ -13,41 +13,47 @@ address**.
1313## Problem
1414
1515Rust (or, rather, LLVM) by default de-duplicates or reuses ** addresses** of ` static ` variables in
16- ` release ` builds. And somewhat in ` debug ` builds, too. For most purposes that is good: The result
17- binary is smaller, and because of more successful cache hits, the execution is faster.
16+ ` release ` builds. And somewhat in ` dev ` (debug) builds, too. For most purposes that is good: The
17+ result binary is smaller, and because of more successful cache hits, the execution is faster.
1818
1919However, that is counter-productive when the code identifies/compares ` static ` data by memory
20- address of the reference (whether a Rust reference/slice, or a pointer/pointer range). For example,
21- an existing Rust/3rd party API may accept ("ordinary") references/slices. You may want to extend
22- that API's protocol/behavior with signalling/special handling when the client sends in your
23- designated ` static ` variable by reference/slice/pointer/pointer range. (Your special handler may
24- cast such references/slices to pointers and compare them by address with
20+ address of the reference (whether a Rust reference/slice, a pointer/pointer range, or the pointer
21+ casted to ` usize ` ). For example, an existing Rust/3rd party API may accept ("ordinary")
22+ references/slices. You may want to extend that API's protocol/behavior with signalling/special
23+ handling when the client sends in your designated ` static ` variable by
24+ reference/slice/pointer/pointer range. (Your special handler may cast such references/slices to
25+ pointers and compare them by address with
2526[ ` core::ptr::eq() ` ] ( https://doc.rust-lang.org/nightly/core/ptr/fn.eq.html ) or
2627[ ` core::ptr::addr_eq() ` ] ( https://doc.rust-lang.org/nightly/core/ptr/fn.addr_eq.html ) .)
2728
2829Then you do ** not want** the client, nor the compiler/LLVM, to reuse/share the memory address of
2930such a designated ` static ` for any other ("ordinary") ` static ` or ` const ` values/expressions, or
30- local numerical/character/byte/string slice literals. That does work out of the box when the client
31- passes a reference/slice defined as ` static ` : each ` static ` gets its own memory space (even with the
32- default ` release ` optimizations). See a test [ ` src/lib.rs ` ->
31+ local numerical/character/byte/string/c-string slice literals. Otherwise an "ordinary" invocation of
32+ the API could trigger your designated signalling unintentionally.
33+
34+ That does work out of the box when the client passes a reference/slice defined as ` static ` : each
35+ ` static ` gets its own memory space (even with the default ` release ` optimizations). See a test
36+ [ ` src/lib.rs ` ->
3337` addresses_unique_between_statics() ` ] ( https://github.com/peter-lyons-kehl/ndd/blob/26d743d9b7bbaf41155e00174f8827efca5d5f32/src/lib.rs#L72 ) .
3438
35- However, there is a problem (in ` release ` mode, and for some types even in ` debug ` mode). It affects
36- ("ordinary") ` const ` values/expressions that equal in value to any ` static ` (which may be your
37- designated ` static ` ). Rust/LLVM re-uses address of one such matching ` static ` ' for references to any
38- equal value(s) defined as ` const ` . See a test [ ` src/lib.rs ` ->
39+ However, there is a problem (caused by de-duplication in ` release ` , and for some types even in `dev
40+ or ` miri ` ). It affects ("ordinary") ` const ` values/expressions that equal in value to any ` static `
41+ (whether it's a ` static ` variable, or a static literal), which may be your designated ` static ` .
42+ Rust/LLVM re-uses address of one such matching ` static ` for references to any equal value(s) defined
43+ as ` const ` . See a test [ ` src/lib.rs ` ->
3944` addresses_not_unique_between_const_and_static() ` ] ( https://github.com/peter-lyons-kehl/ndd/blob/26d743d9b7bbaf41155e00174f8827efca5d5f32/src/lib.rs#L95 ) .
40- Such ` const ` , ` static ` or literal could be in 3rd party code , and private - not even exported (see
41- [ ` cross-crate-demo-problem ` ] ( cross-crate-demo-problem ) )!
45+ Such ` const ` , ` static ` or literal could be in 3rd party, and even in private ( not exported), code
46+ (see [ ` cross_crate_demo_problem ` ] ( cross_crate_demo_problem ) )!
4247
43- Things get worse: ` debug ` builds don't have this consistent:
48+ Things get worse: ` dev ` builds don't have this consistent:
4449
45- - For some types (` u8 ` , numeric primitive-based enums) ` debug ` builds don't reuse ` static ` addresses
50+ - For some types (` u8 ` , numeric primitive-based enums) ` dev ` builds don't reuse ` static ` addresses
4651 for references/slices to ` const ` values. But
47- - For other types (` str ` ), ` debug ` builds do reuse them...
52+ - For other types (` str ` ), ` dev ` builds do reuse them...
4853
49- ` MIRI ` reuses ` static ` addresses even less (than ` debug ` does), but it still does reuse them
50- sometimes - for example, between byte literals (` b"Hello" ` ) and equal string literals (` "Hello" ` ).
54+ ` MIRI ` reuses ` static ` addresses even less (than ` dev ` does), but it still does reuse them sometimes
55+ - for example, between byte (` &CStr ` ) literals (` b"Hello" ` ) and equal string (` &str ` ) literals
56+ (technically, subslices: ` "Hello" ` ).
5157
5258Even worse so: ` release ` builds don't have this consistent. De-duplication across crates depends on
5359"fat" link time optimization (LTO):
@@ -57,6 +63,15 @@ Even worse so: `release` builds don't have this consistent. De-duplication acros
5763lto = " fat"
5864```
5965
66+ For ` dev ` builds cross-crate de-duplication depends on "fat" link time optimization (LTO) AND
67+ ` opt-level ` being 2 or higher:
68+
69+ ``` toml
70+ [profile .dev ]
71+ lto = " fat"
72+ opt-level = 2
73+ ```
74+
6075## Solution
6176
6277` ndd:NonDeDuplicated ` uses
@@ -79,7 +94,7 @@ See a test [`src/lib.rs` ->
7994Use ` ndd::NonDeDuplicated ` to wrap your static data. Use it for (immutable) ` static ` variables only.
8095Do ** not** use it for locals or on heap. That is validated by implementation of
8196[ core::ops::Drop] ( https://doc.rust-lang.org/nightly/core/ops/trait.Drop.html ) , which ` panic ` -s in
82- debug builds.
97+ ` dev ` builds.
8398
8499See unit tests in [ src/lib.rs] ( src/lib.rs ) .
85100
@@ -196,7 +211,8 @@ use an asterisk mask for the minor version, like `0.2.*`. But then you lose auto
196211
197212Functionality of odd-numbered major (` -nightly ` ) versions is always subject to change.
198213
199- The following extra functionality is available on ` 0.3.1-nightly ` :
214+ The following extra functionality is available on ` 0.3.1-nightly ` . You need ` nightly ` Rust toolchain
215+ (of course).
200216
201217#### as_array_of_cells
202218
@@ -212,16 +228,18 @@ Similar to `as_array_of_cells`, `ndd::NonDeDuplicated` has function `as_slice_of
212228
213229#### const Deref and From
214230
215- With ` nightly ` Rust toolchain and use of ` --ignore-rust-version ` you can get
216231[ core::ops::Deref] ( https://doc.rust-lang.org/nightly/core/ops/trait.Deref.html ) and
217- [ core::convert::From] ( https://doc.rust-lang.org/nightly/core/convert/trait.From.html ) implemented as
218- ` const ` . As of mid 2025, ` const ` traits are having high traction in Rust. Hopefully this will be
219- stable not in years, but sooner.
232+ [ core::convert::From] ( https://doc.rust-lang.org/nightly/core/convert/trait.From.html ) are
233+ implemented as ` const ` . As of mid 2025, ` const ` traits are having high traction in Rust. Hopefully
234+ this will be stable not in years, but sooner.
235+
236+ These traits are ** not** implemented in stable versions at all. Why? Because ` ndd ` types are
237+ intended for ` static ` variables, so non-` const ` functions don't help us.
220238
221239## Quality
222240
223241Checks and tests are run by [ GitHub Actions (CI)] ( .github/workflows/main.yml ) . All scripts run on
224- Alpine Linux and are POSIX-compliant.
242+ Alpine Linux and are POSIX-compliant:
225243
226244- ` cargo clippy `
227245- ` cargo fmt --check `
@@ -233,12 +251,25 @@ Alpine Linux and are POSIX-compliant.
233251 - ` rustup +nightly component add miri `
234252 - ` cargo +nightly miri test `
235253- ` release ` -only demonstration:
236- - ` cross-crate-demo-problem/bin/invocation_scripts/static_option_u8.sh `
237- - ` cross-crate-demo-problem/bin/invocation_scripts/static_str.sh `
238- - ` cross-crate-demo-problem/bin/invocation_scripts/literal_str.sh `
239- - ` cross-crate-demo-problem/bin-fat-lto/invocation_scripts/static_option_u8.sh `
240- - ` cross-crate-demo-problem/bin-fat-lto/invocation_scripts/static_str.sh `
241- - ` cross-crate-demo-problem/bin-fat-lto/invocation_scripts/literal_str.sh `
254+ - standard ` dev ` and ` release ` optimizations: most do not get de-duplicated:
255+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh dev literal_str `
256+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh release literal_str `
257+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh dev const_str `
258+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh release const_str `
259+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh dev const_option_u8 `
260+ - ` cross_crate_demo_problem/bin_non_lto/not_deduplicated.sh release const_option_u8 `
261+ - but, some types do get de-duplicated even in standard ` dev ` and ` release ` :
262+ - ` cross_crate_demo_problem/bin_non_lto/deduplicated_out.sh dev const_bytes `
263+ - ` cross_crate_demo_problem/bin_non_lto/deduplicated_out.sh release const_bytes `
264+ - ` release ` with Fat LTO (and ` dev ` with Fat LTO and ` opt-level ` set to ` 2 ` ): deduplicated:
265+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh dev literal_str `
266+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh dev const_str `
267+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh release literal_st ` r
268+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh release const_str `
269+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh dev const_option_u8 `
270+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh release const_option_u8 `
271+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh dev const_bytes `
272+ - ` cross_crate_demo_problem/bin_fat_lto/deduplicated_out.sh release const_bytes `
242273- validate the versioning convention:
243274 - [ ` pre-commit ` ] ( ./pre-commit )
244275
0 commit comments