@tomaka I had started a long answer about “programming, maths and real life”. But as withoutboats reminded it, this is not the point here. I didn’t declare the ellipse-circle problem to be something programming (or more precisely type theory) should be interested in. People did it long before me. Take it as an introduction example for a more generic problem.
@cuviper @nikomatsakis @withoutboats I could have mentioned other known solutions. Of course introducing more specific intermediate traits/interface that represent immutable versions is a possibility. But it’s not perfect. The main problem is that it places a burden (and additional work) on the shoulders of the provider: He/she need to anticipate the need of extracting the const subinterface. Anticipation is difficult. peter_bertok’s example of C# ReadOnly interfaces is relevant. They failed to anticipate and now they can only partially fix the system (or rather they consider it is too much work to modify the language itself to fix the problem). Rust offers you the flexibility to implement on a type you do not own (which is cool) but it does not allow you to define a super-trait for a trait you don’t own. Compare with Go where this issue does not exist as interfaces are structural rather than declarative (I do not mean go’s way to do it is per se a better solution but at least it does not suffer from this precise limitation). Of course the separation could be encouraged as a systematic guideline for developers. Is it the case today in Rust ? Is it done systematically in any language you know ? (and where this would be useful, I’m not talking about Haskell of course)
This is a fairly common pattern in other languages.
Because they have nothing better to offer. Providing the solution naturally built inside the semantics of the language removes the issue. The dev does not have to anticipate a separation of interface. The const trait is available if needed … for free.
but it would introduce a number of complications that don’t quite seem to carry their weight, due to the way that traits work in Rust.
Certainly we have to evaluate the benefit/complexity ratio.
Basically const Foo would have be a separate kind of trait that one can implement.
How would that be a complication? It does not add new constrains. It offers new possibilities.
These would correspond to “at least an ellipse”, “at most an ellipse”, and “exactly an ellipse”.
@glaebhoerl I’m curious about situations where “at most X” would be useful in a programming language. This idea had already crossed my mind but I couldn’t clarify in what situation that would be beneficial.
At the language level, there’s been some speculation about the possibility of “mutability polymorphism”, driven by the concrete use case of e.g. not having to write the same code twice for methods on collections in & as well as &mut versions, so you’d have something like a type-level representation of “shared” and mut you could abstract over. (I am not at all sure this is the right way to go about it, but that’s the most obvious idea.)
Generics for mutability would be a powerful tool IMHO, but I’m afraid that train left a long time ago. Abstracting over such pervasive concepts as mutability may require radical changes on the syntax. But I guess this might be worth it looking deeper into that question.
all items defined by a trait aren’t either &self methods or &mut self methods, there are also self methods, associated functions, associated types, and associated consts. How would it be determined which of these is included by a T: const Trait bound?
@withoutboats, aren’t they exciting questions ?
But sure a more complete analyse is needed. I will try to propose one.
I think it would be fairly easy (possibly even backward compatible) to refactor a single trait into multiple traits.
Again only if you own the type.