This question may sound a little immature but I have only working experience with python programming and I would like to ask why rust developers choose to keep turbofish and dot operators to access methods in language?
Please do direct me to the sources if there are subtle differences between these two rotations, I would like to learn more. Thank you so much
To add on to this, Python has very little distinction between compile-time and run-time. Classes and modules can be modified during program runtime, and thus "accessing a function in a module" is a very similar operation to "accessing a value of an object". Classes in python are values themselves, and thus getting a function of a class is similar to calling a method on an instance of that class.
In contrast, rust is pre-compiled and has a much stricter difference between things that can be accessed at compile time, and things that are using runtime data to retrieve. :: always refers to finding something that is known at compile time, like a type, or a function, or a module. . on the other hand accessing things from some value that only exists at runtime. MyStruct::my_func() uses :: because the "value" MyStruct::my_func is a function which is defined and known at compile time. let x = MyStruct::new(); x.my_method(); uses . because x is a value here (not a type), and calling my_method will require passing in x's self at runtime.
This explanation isn't entirely accurate, as we still use . to access fields of constant values, and those are then also constants. It's more the difference between "types / namespaces" and "values" than between compile-time-known-things and runtime-known-things. But since types and namespaces are always known at compile time, I think it's a useful enough analogy.
I'm not a specialist in Rust nor I know the exact rational for this syntax, but I like to think it like this:
:: is for types and . is for values. Just like in functions, < > is for type parameters and ( ) is for value parameters. Rust likes to keep those two contexts separate.
Besides, the :: has other uses, also connected to types.
It can disambiguate when a method exists in multiple traits. For example, the I type is an iterator and also implements my trait Linked. We must tell the compiler which next we want to use every time.
trait Linked {
fn next(&self) -> Self;
}
fn my_func<T, I: Iterator<Item=T> + Linked>(mut values: I) {
let next_value = Iterator::next(&mut values).unwrap();
let next_iterator = Linked::next(&values);
// next_value: T and next_iterator: I
}
The :: is used in the "turbo-fish" syntax, to tell the compiler which type parameter to use (either because you want to be explicit about it or because it's needed.
fn my_func() {
let not_fine = "17".parse().unwrap(); // which type are you parsing to?
let compiles: i32 = "17".parse().unwrap();
let also_compiles = "17".parse::<i32>().unwrap();
let compiles_but_not_needed: i32 = "17".parse::<i32>().unwrap();
use std::collections::HashSet;
let total_unique: u64 = vec![1, 2, 3, 2].into_iter().collect::<HashSet<u64>>().into_iter().sum();
}
How would the compiler know we wanted to construct a HashSet to keep only the unique values?
I'm not sure, but from your initial question it seems like you may be confusing the turbofish ::< > with the more general :: syntax. I don't know if Rust has a documented name for :: though.