←back to thread

128 points RGBCube | 6 comments | | HN request time: 0s | source | bottom
Show context
jhugo ◴[] No.44498376[source]
> we cannot just require all generic parameters to be Clone, as we cannot assume they are used in such a way that requires them to be cloned.

No, this is backwards. We have to require all generic parameters are Clone, as we cannot assume that any are not used in a way that requires them to be Clone.

> The reason this is the way it is is probably because Rust's type system wasn't powerful enough for this to be implemented back in the pre-1.0 days. Or it was just a simple oversight that got stabilized.

The type system can't know whether you call `T::clone()` in a method somewhere.

replies(4): >>44498401 #>>44498749 #>>44499040 #>>44499325 #
berkes ◴[] No.44498401[source]
> The type system can't know whether you call `T::clone()` in a method somewhere.

Why not?

replies(1): >>44498701 #
1. jhugo ◴[] No.44498701[source]
Types don't carry behavioral information about what the method does internally. Everything about a method is known from its signature.

The compiler doesn't introspect the code inside the method and add additional hidden information to its signature (and it would be difficult to reason about a compiler that did).

replies(2): >>44498750 #>>44498864 #
2. delta_p_delta_x ◴[] No.44498750[source]
> Types don't carry behavioral information about what the method does internally.

I was under the impression type inference meant that the implementation of a function directly determines the return type of a function, and therefore its signature and type.

replies(3): >>44498799 #>>44498834 #>>44498836 #
3. jhugo ◴[] No.44498834[source]
While you can sometimes elide the return type (and what you describe only happens in closures — `|| { 0u32 }` is the same as `|| -> u32 { 0u32 }` — methods and free functions must always have an explicitly declared return type), that's not the same thing as being described above.

For the existence of any invocation of `<T as Clone>::clone()` in the method body to be encoded in the method signature, we'd either need some wild new syntax, or the compiler would need to be able to encode hidden information into types beyond what is visible to the programmer, which would make it very hard to reason about its behavior.

4. mryall ◴[] No.44498836[source]
No, Rust functions have to declare their return types. They cannot be inferred.
5. ninkendo ◴[] No.44498864[source]
> Types don't carry behavioral information about what the method does internally.

I don’t remember specifics, but I very distinctly remember changing a method in some way and Rust determining that my type is now not Send, and getting errors very far away in the codebase because of it.[0]

If I have time in a bit I’ll try and reproduce it, but I think Send conformance may be an exception to your statement, particularly around async code. (It also may have been a compiler bug.)

[0] It had something to do with carrying something across an await boundary, and if I got rid of the async call it went back to being Send again. I didn’t change the signature, it was an async method in both cases.

replies(2): >>44499134 #>>44499138 #
6. kd5bjo ◴[] No.44499138[source]
`Send`, `Sync`, and `Unpin` are special because they're so-called 'auto traits': The compiler automatically implements them for all compound types whose fields also implement those traits. That turns out to be a double-edged sword: The automatic implementation makes them pervasive in a way that `Clone` or `Debug` could never be, but it also means that changes which might be otherwise private can have unintended far-reaching effects.

In your case, what happens is that async code conceptually generates an `enum` with one variant per await point which contains the locals held across that point, and it's this enum that actually gets returned from the async method/block and implements the `Future` trait.