What is the meaning of this code?

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.

5 Likes

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?

1 Like

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:?}");
}

(Playground)

Output:

number = 0
string = ""

1 Like

Corresponding matches regarding the turbofish syntax in the Rust reference:

1 Like

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

2 Likes

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)

  • crate_name :: item_in_the_crate
  • module :: item_in_the_module
  • TypeName :: associated_item or TraitName :: associated_item
    • an associated item could be a method or function, but also a constant, or a type
  • <complicated type expression> :: associated_item
  • <Type as Trait…> :: associated_item
  • function :: <lifetimes, constants, Type parameters, …>
  • expression . method :: <lifetimes, constants, Type parameters, …>

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,

  1. rand is a lowercase identifier,
  2. random also is a lowercase identifier, and
  3. <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

  • variables only if the path is short without :: symbols
  • modules or crates precisely if there’s further identifiers later in the same path after :: symbols
    • crate names can only appear at the beginning of a path
  • function names if they aren’t followed by anything or only by a <>-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 name

For 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.)

5 Likes

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.