Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

> Knowing the type of a variable is not always helpful during normal reading (e.g. the types of iterators in C++ or Rust).

Not only that, but there are some types in Rust that you _can't_ even specify; closures specifically don't have concrete types that can be specified in a Rust program.



sort by: page size:

Perhaps it's due to the fact that you can't really have type-generic closures in Rust: once the compiler has inferred one type, the closure cannot be used with another type. In contrast, inner functions can have any number of additional type parameters.

> In Rust and Haskell you have to at least annotate the parameter types and return type of functions.

In Rust you do, but in Haskell you don't.


> is the lack of explicit type declarations

This is mistaken. Rust has explicit type declarations, augmented with type inference. Furthermore, unlike popular statically-typed functional languages, Rust's type inference is deliberately restricted to only operate within functions (intraprocedural) rather than between functions (interprocedural), which means that one always has explicit types available in the function signature when reading code.


It's not even factual.

> In Rust and Haskell you have to at least annotate the parameter types and return type of functions. Type inference is only for variable bindings inside the function body.

This is false for Haskell. Can't speak for Rust.


> With an interface, anyone else could come along and create their own implementation.

Not true in Rust, because the type returned by the function is a concrete type.

I think we may be overloading “name” here, a bit. If you return an “impl” in Rust, you have to name an interface, but you can’t actually create new instances of the concrete type returned by the function.


> every method on collections like `map` or `filter`

I think you're remembering the various iterator adaptors, but they aren't methods on collections, they work on Iterators as their name implies. Somebody else explained why these methods can't all just return "Iterator" (that isn't a type) but just thought I'd mention you won't find map and filter in Rust's Vec or HashMap types, those methods live in the Iterator trait.


Not having type inference is a non-starter in Rust. Lifetimes would be totally impractical, and you couldn't use closures.

This seems like too narrow a reading of OP's point. Rust imposes not only types, but also a particular tree-like program structure which makes it awkward to express lists, upward references, and other constructs.

I think this is why Rust's type inference only applies within a function.

The article is about Rust. In Rust, the type of the closure indicates whether it can mutate externally visible data (and therefore race on it).

> What problem would higher kinded types solve in Rust?

You don't have a way to say in Rust 'this functions works on types similar to an 'Option<T>' for all types T. Or 'this functions works on types similar to a 'LinkedList<T>' for all types T. Which actually is more or less the same, we can therefore name both 'Option' and 'LinkedList' for example, well, 'Mona'.


> Like, if I want to parse command-line arguments.

What command-line parsing libraries have “a bunch of unnecessary type parameters”?

Only library I can think of which even has them would be structopt, and that’s not unnecessary, the entire point of the library is to deserialise to a structured type.

And of course rust was designed from the ground up to leverage generics, e.g. by design it does not have reflection / RTTI, interactions with variable or foreign types are thus generics based (often, sometimes trait objects will do though they have their own tradeoffs), or it doesn’t have overloading so while not handing all overloading use cases by a long shot it uses conversion traits (aka generics) for “convenience” overloads e.g. foo(string) and foo(path) become foo<T>(_: T) where T: AsRef<Path>.


You're not wrong, but I don't really see the problem? Even well before async Rust, closures worked the same way with not being able to specify a concrete type, and `impl Trait` syntax didn't even exist for a while. Annotating local variable types is a way to fix certain things that would otherwise be ambiguous; it's a means to an end, only an end itself.

Rust has actual type inference. It just happens to be a local affair. For example, you can create a vector, even an empty one, without explicitly specific its element type. But, somewhere in the current function, you have to provide information about the element type, say, by inserting elements into the vector, or by using an element in a way that constrains its type.

Closure type inference doesn't work well enough in Rust, so your refactoring will not compile unfortunately. Rust needs the closures to be used inline.

> there is no general type of Rust lambdas that take an A and return a B other than the trait object type `dyn impl Fn(A) -> B` (Or FnMut or FnOnce);

I don't understand the issue (and I know very little of rust), but isn't `dyn impl Fn(A) -> B` exactly that type? Any language with first class function parameters and objects, but type erases them (unlike rust and c++) would implement them pretty much like the dyn impl above under the hood.


If it's not clear that the compiler will infer the correct type, you can be specific. From the context, there's only one possible type though so that's redundant. Unlike C, older C++, or older Java, Rust expressions aren't necessarily fully typed in isolation. It's quite normal for a function to have an independently-generic return type and for the final type to be provided solely by context. Type inference only happens within named functions though: you need to specify your function signature.

Even if you find yourself wanting to know what the type actually is, the compiler (in its guise as a language server) can tell you that and let your IDE show you, rather than requiring you to type it out yourself.


Thanks! I just thought their might be some quite important technical reasons for this decision.

After all overloading the return type is quite usual and extremly helpful for parsing or the conversion of types.

But these kind of operations most likely will be done with a Trait in Rust, so having this option for closures might not be that important.


Although that's true, and potentially useful as a feature (not sure I want it, but pretty sure I wouldn't be angry if it was added)..

I don't think that's what your parent was talking about when they say "couldn't name the actual type".

They're saying, suppose I have a function that says it return an Iterator and I realise ah, it's not really returning an Iterator, but some subtype of Iterator - or, in Rust where subtypes don't exist, a type which merely implements Iterator. I now can't talk about the actual type being returned, because that's hidden from me, and it needn't be.

next

Legal | privacy