I am just starting to use Rust, under the impression of its ownership paradigm. Reading the tutorial, I found the following code
let secret_number = rand::thread_rng().gen_range(1, 101);
I assume that a random integer is generated after instantiating of the rand::ThreadRng
structure by calling its method.
Isn't it better for performance simply to use functions like in C? Since Rust positions itself as a system programming language and if I am right in my conjecture, this choice in favor of creating structures and using their methods seems to be quite suboptimal.
Is there any overhead in Rust-style method calling approach compared to [...] C?
No.
The exact same machine instructions can be generated by both two languages. The syntax to express the concepts does not require that the resulting code be inefficient (and in many cases the opposite is true).
Isn't it better for performance simply to use functions like in C?
Methods are functions.
A Rust method is conceptually the same as a C function taking a pointer:
Rust
struct Foo {
a: i32,
}
impl Foo {
fn add(&self, b: i32) -> i32 {
self.a + b
}
}
C
struct Foo {
int a;
};
int Foo_add(struct Foo *self, int b) {
return self->a + b;
}
There's no important difference between the two languages here.
Rust has free functions as well as associated functions; if you don't need to take a reference to data, you don't need to:
Rust
struct Foo {
a: i32,
}
// Free function
fn foo() -> Foo {
Foo { a: 42 }
}
impl Foo {
// Associated function
fn new() -> Foo {
Foo { a: 99 }
}
}
C
struct Foo {
int a;
};
struct Foo foo() {
struct Foo foo = { 42 };
return foo;
}
struct Foo Foo_new() {
struct Foo foo = { 99 };
return foo;
}
Rust also has zero sized types which look like they have associated data but which disappears in the compiled code:
Rust
// Zero size
struct Foo;
impl Foo {
fn add_one(&self, b: i32) -> i32 {
b + 1
}
}
C
int Foo_add_one(int b) {
return b + 1;
}
creating structures and using their methods seems to be quite suboptimal
Creating structures and having related methods is common in C code. I'd say that it's most likely the predominant coding style; I don't know why you'd say it's suboptimal.
Your example case isn't useful to compare because you haven't provided any C code, much less something equivalent. ThreadRng
does a lot of work to provide a better experience for generating random numbers:
ThreadRng
usesReseedingRng
wrapping the same PRNG asStdRng
, which is reseeded after generating 32 MiB of random data. A single instance is cached per thread and the returnedThreadRng
is a reference to this instance — henceThreadRng
is neitherSend
norSync
but is safe to use within a single thread. This RNG is seeded and reseeded viaEntropyRng
as required.
The "traditional" random number generator (rand
) has a global state (which is generally a bad thing), but conceptually looks like:
struct RngState {
int whatever_goes_here;
};
static struct RngState GLOBAL_STATE = { 0 };
int rand_with_state(struct RngState *state) {
return state->whatever_goes_here++;
}
int rand() {
return rand_with_state(&GLOBAL_STATE);
}
Note that it still uses a pointer to the data.