Displaying -- rand::thread_rng -- using -- println!()

Hello,

The problem:

println!("{:?}",rand::thread_rng());

The question :

It displays this – ThreadRng { rng: UnsafeCell { .. } } – in the cmd, therefore, what would be the meaning of UnsafeCell ? Shouldn't is be displaying random numbers ?

Thanks for the help

Other related question:

Is it possible to write this:

let secret_number = rand::thread_rng().gen_range(1..=100);

like this:

let secret_number = rand::thread_rng(gen_range(1..=100));

?

Thanks and have a good day

rand::thread_rng() returns the random number generator itself. You need to call methods like .gen() or .gen_range() on it to fetch random numbers from the generator.

No. If you just need a random number without restricting the range, you can use rand::random() as shorthand for rand::thread_rng().rand() however.

1 Like

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 derived, or output when a program crashes, it seems like unintentionally printing internal states of RNGs could potentially be a big problem.

2 Likes

Thank you for the answer, I will excplicitate my question further in a next text

Thank you for this very detailed answer.

My big problem is that I don't understand the chaining of methods or functions.

For example, if there is let a = method1().method2()

does it mean that we have method2(method1()) or method1(method2())

I'm still reading the rust book and stuck on rustling exercise 29 with no prior programming knowledge, there are some times certain things that seem strange.

for example, when I try to do println!("{:?}",rand::thread_rng(gen_range(1..=100)));

it says that there is an extra argument, however I always believed that method chaining was about inputing the result of the first function or method in the argument of the second function or method by using dots. For example method1().method2() being the same as method2(method1())

Thank you again for all your answers and I apologize for my lack of prior required knowledge. I'll take any advice regarding the ways of consolidating a fundamental understanding of programming. Feel free to tell me about any ressources outside the rust book that would explain such things because it seems that I really lack the much needed knowledge to understand the answers.

It’s the former: method1().method2() is comparable to method2(method1()).

In fact, you can usually even use syntax like that as an optional alternative (though you’ll have to specify the type the method is defined on as a prefix).

use rand::{rngs::ThreadRng, thread_rng, Rng};

fn main() {
    println!("{:?}", ThreadRng::gen_range(&mut rand::thread_rng(), 1..=100));
}

(playground)

For trait methods like gen_range there’s actually a bit of a variety of different kinds of things you could but to the left of the ::; either the type, or the trait, or both. To use the method of the

impl Rng for ThreadRng { … }

[1] you could write

ThreadRng::gen_range

or

Rng::gen_range

or

<ThreadRng as Rng>::gen_range

Apart from the way of naming the method, the other factor is the handling of the arguments. A method-call of foo.bar(baz, qux) generally corresponds to Type::bar(foo, baz, qux) so the thing originally on the left side corresponds to the first argument. But there’s additionally a mechanism of automatically including steps such as

  • taking a reference (e.g. using Type::bar(&foo, baz, qux) or Type::bar(&mut foo, baz, qux))
  • dereferencing or a combination of the two (which can result e.g. in adapting the self argument from &i32 to i32; or from &mut T to &T, or &Vec<T> to &[T], etc…)

that can happen in order to make the type match, which is why the playground above uses

ThreadRng::gen_range(&mut rand::thread_rng(), 1..=100))

with the additional &mut …

For more examples & another introduction to this feature feel free to check out this section in the book.


  1. more precisely, this case actually has an impl RngCore for ThreadRng { … } here paired with a blanket impl to lift this to the Rng trait which has the gen_range method ↩︎

2 Likes

Thank you very much for this disambiguation about the syntax for

println!("{:?}", ThreadRng::gen_range(&mut rand::thread_rng(), 1..=100));

It was very useful because now I know that 1..100 was not the only argument for gen_range() and that thread_rng() was also there, it feels much better to know these syntactic elements.

There are other elements which will require more reading from me, however the main thing about methods has been understood, it was a very useful answer. I'll use this again in the future for learning about the other mentioned elements like the thing with the :: and what it means from context to context (I always believed it was just used to import a prelude).

Thank you for these useful informations and have a good day