rand::random::<u16>()
why is there no method name after ::?
rand::random::<u16>()
why is there no method name after ::?
In Rust, the ?
operator is used for error handling and is typically used with methods that return a Result
type. When the ?
operator is used with a method call, it automatically propagates the error returned by the method up to the caller, unless the error is explicitly handled using a match
or Result
-handling construct.
In the code snippet you provided, rand::random::<u16>()
does not return a Result
type. Therefore, there is no error that needs to be handled and the ?
operator is not required. If a method that returns a Result
type were to be called, then the ?
operator could be used to propagate any error that occurs up to the caller of the method.
The (free-standing, "static") rand::random
function has a generic return type, so the ::<u16>
is specifying that it returns u16
. The syntax is known as "turbofish", and doesn't intrinsically have anything to do with structs or methods, it's equally applicable to anything that takes generic parameters.
I assume you somehow misread the original post, there was no question about the “?
” operator here, as far as I can tell. Maybe you mistook the question mark at the end of the question
why is there no method name after ::?
as Rust syntax?
A (minimal) example:
fn foo<T>() -> T
where
T: Default,
{
T::default()
}
fn main() {
let number = foo::<i32>();
let string = foo::<String>();
println!("number = {number:?}");
println!("string = {string:?}");
}
Output:
number = 0
string = ""
Corresponding matches regarding the turbofish syntax in the Rust reference:
that's the syntax for specifying generic parameters in expressions
most languages would do it like rand::random<u16>
, but that's surprisingly difficult to parse, so rust uses a ::
before, it's called the turbofish
rand::random
is generic, because it may generate any generate any type that can be randomized, and the way you tell it what type to generate is through the generic parameter
I’ll try to give some more context for interpreting something like rand::random::<u16>()
.
First on your wording… The whole thing rand::random::<u16>
is a path, and followed by “()
” it creates an expressions like the expressions foo(args…)
or bar::baz(args…)
which are usually called function calls, whereas method calls would contain a dot (“.
”) followed by the method name, e.g. x.foo(args…)
. So the idea of (calling) a “method” (which is generally taken to mean an associated function with a self
receiver argument) does not really apply, because we don’t only have no method name but also no “.
”.
So in rand::random::<u16>()
, rand::random::<u16>
should be the path of some function. Let’s try to make more sense of this path.
Paths typically denote either some globally defined thing or a local variable, in the latter case you would only see a simple path without any ::
symbols though. ::
can denote a number of different relationships, typically the options include (off the top of my head)
the TL;DR of such a listing of course is, there’s lot of meanings to ::
, and it’s easy to get confused if you have only seen limited use.
Let’s look at the components of the path in question at an (almost) lexical level: this path is consisting of three ::
-delimited parts,
rand
is a lowercase identifier,random
also is a lowercase identifier, and<u16>
is a type (u16
) enclosed by “pointy brackets” (although ASCII would tell you these are actually less-than and greater-than signs…)Now, the <>
brackets are always quite clear in denoting some type-like information (and u16
is a well-known type). Lower-case identifiers in simple paths can generally be local variables, modules, crates, or functions, they tend to be
::
symbols::
symbols
<>
-delimited section as in the example.So the path in question has
rand
as a module or crate name (in this case presumably the crate rand
)random
as a function nameFor paths, modules and crates work really similarly, so let’s look at an example how this path could come up with rand
being a module
mod rand {
pub fn random<T>() -> T { … }
}
fn demo_call() {
rand::random::<u16>()
}
(the real random
method has some trait bounds, too, but those are irrelevant for calling syntax)
The idea of random<T>
is that it is a single generic function, which is like saying, for each type Foo
, there exists a version of the function, random<Foo>
, with the signature fn() -> Foo
. The idea how to read rand::random::<u16>
above is that it it denotes the random<u16>
version (aka instantiation) of this function in the module rand
. Intuitively, one would perhaps want to write this as rand::random<u16>
(and that’s AFAIK the general syntax idea that languages like C++ or Java follow for generics), but there’s some downsides: As hinted at above
ASCII would tell you these are actually less-than and greater-than signs…
the symbols <
and >
are actually not real “brackets” and they have multiple meanings in Rust, also being used as true less-than and greater-than signs. This motivates the language design choice of adding an additional ::
for disambiguation, otherwise foo::bar<baz>
looks like it starts with the comparison expression foo::bar < baz
. (It becomes even more problematic, once commas are involved.)
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.