Allow default implementation and properties in interfaces

Hi, I have come to rust from swift and noticed a few complaints about how things are here. The biggest problem I have in rust is that traits cannot have a default implementation. Frequently, when designing a library (or any piece of software in fact) the ability to give trait a default implementation would be very useful in terms of code reuse, given the fact that rust doesn't have inheritance besides impl blocks. So in situations like the following, it would be very convenient to give some traits default implementation, which effectively makes them mixins.

Unfortunately, rust, contrary to swift, doesn't have the ability to declare a field in interfaces. I assume the problem is that people think that it is not possible to embed a field into type after it was created. They are wrong. In swift, the interfaces (or protocols in its parlance) could be declared as such:

protocol SomeThing {
  var someThing: Type { get set }
  static var anotherThing: Type { get }
}
//in rust it could be
trait SomeThing {
  let mut someThing: Type { get set } //instance member
  static let anotherThing: Type { get } //type member
}
//the only restriction to pose on this, I think, 
//is to prevent the ability to () self out

This thing also would require manual collision resolution if implemented, but that's not a problem; many other languages tackled this issue.

Some vague example of how it can be useful

trait GeometricFigure {
    type Shape: TopologyDescriptor
    type Space: MetricSpaceDescriptor

    let mut area: Space.PointDistance { get } //think int or such
}
impl GeometricFigure 
where 
    Descriptor: <#Basically shape with 4 sides for ex.#>
{
    let mut area: i32 { return <#Calculate area here#> } //new syntax for computed properties
}
//now if you had used it to create a custom shape with 4 sides, 
//it would receive the ability to compute its area for free
struct Square {}
impl GeometricFigure for Square { <#No need to write it all over again to get area#> }

There already was a discussion for this (here and here and god knows where else), but both died. Wanna resurrect it and see what changed since then. It is actually interesting to know why this feature is not in language still.

1 Like

Methods in traits can have a default implementation. Just use a method instead.

2 Likes

Is there a particular reason to not to use fields in traits, though?

1 Like

The short version of the problem is that Swift's var field: Type { get set } syntax is sugar over a getter/setter pair.

In Rust, though, a field access guarantees that it's just a field access, and thus very cheap. Thus hiding potentially arbitrarily expensive method calls behind field access syntax is a huge footgun that Rust would like to avoid.

Add on top of this that field access has more permutations than in Swift's class/struct model: get-by-move, get-by-shared-ref, get-by-mut-ref, set-by-move; and it becomes a lot harder to "just" have field accessors in traits.

(Even in the case where we do eventually add fields in traits, they'd still always refer to real fields rather than calculated values / method sugar.)

So use method access to say what you mean, and you can provide a default implementation in the trait for all instances of the trait. See for example all of the functionality that Iterator gives you for just implementing Iterator::next.


(Side note: because of Swift's ABI stability without structure freezing, field access to a library type is always a method call. That makes the incremental cost of "field syntax getters" a lot smaller, because all fields are already getters anyway. Think of the restrictions that @frozen implies in Swift; most of them always apply to Rust code. (The biggest exception being able to update structure members with a nonpublic field, as ABI does not need to be preserved, because Rust is statically linked.))

11 Likes

Note that fields in traits were proposed in this RFC: https://github.com/rust-lang/rfcs/pull/1546

But the proposal was postponed because it required a substantial amount of work to sort out all related issues and there was no bandwidth for that.

2 Likes