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.
:: 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
x is a value here (not a type), and calling
my_method will require passing in
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.