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
uses ReseedingRng
wrapping the same PRNG as StdRng
, which is reseeded after generating 32 MiB of random data. A single instance is cached per thread and the returned ThreadRng
is a reference to this instance — hence ThreadRng
is neither Send
nor Sync
but is safe to use within a single thread. This RNG is seeded and reseeded via EntropyRng
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.