Hi there! FYI, I’ve edited your post a to make the code sections properly formatted. For more information, feel free to check out this pinned thread about the topic.
If you look at how ThreadRng
is implemented, e.g. by clicking the “source” link in the top-right of its documentation page (taking you here)
#[derive(Clone, Debug)]
pub struct ThreadRng {
// Rc is explicitly !Send and !Sync
rng: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>>,
}
then you can see that there’s a “#[derive(Debug)]
” on this struct. This has the effect that the debug-printing output of this struct uses the default implementation of just delegating to the field types.
This is what this output comes from… these debug-printing outputs are deliberately not meant to be used in production code, since – as you can see here – they do make visible some implementation details. Furthermore, in this case it’s also not the mose useful output anyway. This is because how the internals of ThreadRng
use some unsafe-to-use shared-mutability primitive (the type UnsafeCell
) which supports the Debug
-printing in a way that doesn’t actually display any of its contents. This is because of how all access to the contents of UnsafeCell
is unsafe
(typically through the raw-pointer returned from its get
method) and requires manual reasoning from the user to make sure to avoid unsound mutable aliasing.
This all should be mostly irrelevant for you as a user. Realistically, the TL;DR of the above investigation is simply the following:
- the implementors of
ThreadRng
didn’t really think about providing all-that-useful of a Debug
-printing experience, by taking the common approach of just slapping derive
for Debug
on it
- as a user, this means that there isn’t really any useful output from
ThreadRng
for you to see
- it does seem likely that a less confusing alternative output seems desirable, even in the view of the maintainers of
rand
- looking into this last point, if we look at the beta version of
rand
0.9
, we can find that there, the issue is actually already addressed:
- you can find a manual implementation there, though it doesn’t give you more functionality; it simply prints
ThreadRng { .. }
, hiding the rng: UnsafeCell { .. }
field
- I’m not an expert on the relevant design and safety considerations, but IMO it seems probably desirable that there’s no easy way to read out the internal state of a random number generator without need
in other words… regarding the question of “what possibly useful information could be output?” your idea was
And that doesn’t really fit with what a random number generator is internally. Instead, there’s going to be some small collection of numbers that specify the internal state of the generator; which is distinct from its output of calling its method for calculating the next “random” number and updating the state.
The following is some context on my intuition behind my sentence above that “IMO it seems probably desirable that there’s no easy way to read out the internal state”:
Random numbers have applications in cryptography, sometimes it’s quite desirable to make an RNG in a way that just observing the generator gives no good clues as to the internal state of the generator (if it did, an attacker could potentially predict future outputs of the same generator; and that’s a problem e.g. if the RNG is then used to generate random keys for encryption, or the like). Thus, it doesn’t surprise me if an RNG API tries hard not to expose any clues about the internal state of the RNG over any other channels either, without need; especially giving common practice of Debug
being derive
d, or output when a program crashes, it seems like unintentionally printing internal states of RNGs could potentially be a big problem.