Hi Andres,

I'm thinking about your question from a structural typing point of view, so
let me see if an analogy from Scala (which has its own take on traits)
helps. Scala is more of an OO language than Rust is, and Scala traits can
extend classes (abstract or not), which is how it's possible for a trait to
incorporate members (what Niko just described as "struct inheritance") --
but the trait itself cannot name a new member, only methods, as with Rust.
So in that regard, traits are also an awful lot like Go's interfaces, in
that they describe the interface that a value must be able to satisfy at
compile time. All three languages are structurally typed, but Go has the
fewest bells and whistles in the type system, Scala the most, and Rust
somewhere in the middle (thanks to generics and type bounds). There are
efficiency reasons for that; it's relevant that Scala is a JVM language.

This isn't a particularly concrete answer, but I hope it helps.

Cheers,
--mlp


On Fri, Sep 20, 2013 at 2:18 PM, <andres.osin...@gmail.com> wrote:

> It would be audacious to propose this as a change; I'm merely trying to
> understand the philosophy behind certain design decisions,
>
> My personal motivation comes from an interest in seeing how expressive
> Rust can be as a language to model business objects; I think the
> intersection between high-level logic and type classes with low-level
> access, extreme efficiency, and deterministic memory access, is a very
> interesting domain which, thus far, no language manages to handle well, and
> Rust shows immense promise in that.
>
> Something I really dislike and consider a huge antipattern is the question
> of logical indirection and boilerplate; anything that hinders the
> understanding of the code through unnecessary invocations or synax is a big
> no-no for me (a great example being Java's requirement for type
> declarations everywhere and the disgusting use of accessor methods
> everywhere even when they make no sense).
>
> Rust is a very lean language and seems to be extremely concise for what
> it's capable of. And the thing is, if a trait has a data dependency rather
> than a behavior (method existence) dependency, then why are we working
> around the data through methods when it just happens to be that what we
> really want is to fetch the data in a struct with no logical
> intermediaries, regardless of whether the compiler turns that into a
> straight memory access?
>
> The first use case that came to mind was when I was attempting to create a
> business object library that could be used a starting point for forms, data
> persistency, serialization, validation, and the like. I wanted to define,
> for a model, a series of validators that would depend on the struct
> definition; something easy to do with Enums and macros. However when it
> came to validation, i wanted to store certain conditions in a data
> structure, such that all models have a validate() method that would query
> the data needed to perform validation.
>
> The current trait implementation would require me to needlessly define a
> getter/setter for the validation data structure in order to have a trait.
> In reality, i just want to define the trait as a the existence of a certain
> struct member data definition (a corresponding validators data structure),
> with no limits in how I may want to access of modify such data.
>
> The beauty of this would be that i would only need to define said member
> and trait in order to get all the features i could ever want out of a
> validation library, and default methods would take care of the rest,
> whereas as it's now, i would have to define the accessors, or use a macro
> so that any model definition would come with an automatic accessor
> definition. This is not ideal if I have a system with several hundred
> models, which may be trivial in their definition but require a useless
> method definition for each of them.
>
> And while I think it's great that a macro is capable of that, it seems to
> me that as a language feature it would be substantially more useful.
>
> Like I said before, I'm just toying around; I have little frame of
> reference as to whether there are hidden downsides to such a feature. I
> just wanted to know what the community thinks about this.
>
> Thanks
> Enviado desde mi BlackBerry de Movistar (http://www.movistar.com.ar)
> ------------------------------
> *From: * "Felix S. Klock II" <pnkfe...@mozilla.com>
> *Date: *Fri, 20 Sep 2013 13:41:02 +0200
> *To: *Andres Osinski<andres.osin...@gmail.com>
> *Cc: *rust-dev@mozilla.org<rust-dev@mozilla.org>
> *Subject: *Re: [rust-dev] Struct members in trait definitions
>
> Andres (cc'ing rust-dev)-
>
> An initial question, since I'm not clear on one thing:
>
> What is your goal in proposing this change?
>
> That is, is your primary concern that you dislike writing either method
> invocations or method definitions?  Or are you concerned about the ability
> of the compiler to optimize the generated code if one uses methods instead
> of struct fields?
>
> ----
>
> Justifications for why traits should be expressed in terms of associated
> methods, not associated fields (and thus why Rust does things this way):
>
> 1.) Method definitions are strictly more general than fields, in terms of
> allowing other implementations to dynamically compute the value, read it
> from a database, from an input stream, etc).  I assume you already are
> aware of this, and just want to know why we don't provide special handling
> for Trait designers willing to rule out such generality up-front.
>
> 2.) Associated struct-fields would either disallow mixing traits whose
> names collide, or would require extending the `impl` item syntax with a
> struct-field renaming feature.
>
> Elaboration of point 2:
>
> Traits are designed to be mixed together; the language should discourage
> patterns that make mixing traits on a single type difficult.
>
> The fields of a struct are written down with the `struct` definition.
>
> The associated methods for an implementation are written down with the
> `impl` item.
>
> If two traits require the same kind of associated state, right now you
> would give them identical method names, and the one struct could implement
> both traits (i.e. mixing them) with no ambiguity.
>
> If traits were to define struct names, to get the same amount of
> generality we would need to provide some way to map the field name of the
> struct to the name expected by the trait within `impl` items.  But why do
> that?  A method definition is a perfectly reasonable way to express this.
>
> ----
>
> Concrete illustration of point 2 above: How would you express the below in
> your proposal, assuming that *both* T1 and T2 are revised to require
> `state` to be a member field rather than a method?
>
> ```rust
> trait T1 { fn state(&self) -> int; }
>
> trait T2 { fn state(&self) -> int; }
>
> struct one_int { state: int }
>
> struct two_ints { state: int, state2: int }
>
> impl T1 for one_int { fn state(&self) -> int { self.state } }
> impl T2 for one_int { fn state(&self) -> int { self.state } }
>
> impl T1 for two_ints { fn state(&self) -> int { self.state } }
> impl T2 for two_ints { fn state(&self) -> int { self.state2 } }
> ```
>
> ----
>
> Again, to be clear: I'm not saying its impossible to express the example
> above via hypothetical Traits with fields.  But I think it would add
> unnecessary complexity (e.g. extensions to `impl` item syntax).  So that's
> why I wanted to know what the driving motivation here is.
>
> If the motivation is concern over syntactic overhead: method invocations
> vs field deference seems trivial.  The method definitions are more annoying
> boilerplate code, but I imagine that one could write an easy macro_rules!
> for the common case where the Trait method name is the same as the struct
> field name.
>
> If the motivation is concern over the quality of the generated code: I
> assume that LLVM does a good job inlining these things.  (If I'm wrong
> about that, I'd like to know.)
>
> Cheers,
> -Felix
>
>
> On 20/09/2013 13:02, Andres Osinski wrote:
>
> Hi all, I have a question which I'm sure must have already been discussed
> and dealt with, but I wanted to understand the design rationale:
>
>  A lot of trait-level functionality would be enhanced if a trait could
> specify members to be included in the struct which implements the trait.
> This can be solved in practice by wrapping member access in accessor
> methods, but I fail to see why that would be preferable.
>
>  The reason I'm asking is because I'm trying to design data structures
> which contain a couple of arrays, and I wanted to define the trait by not
> only a set of supported operations but by the existence of both arrays so
> that a default method could deal with any struct which implements the
> trait, instead of having to define for every struct an accessor method for
> each structure and then have to call the accessors in the trait to do
> anything.
>
>  Thanks
>
>  --
> Andrés Osinski
> http://www.andresosinski.com.ar/
>
>
> _______________________________________________
> Rust-dev mailing 
> listRust-dev@mozilla.orghttps://mail.mozilla.org/listinfo/rust-dev
>
>
>
> --
> irc: pnkfelix on irc.mozilla.org
> email: {fklock, pnkfelix}@mozilla.com
>
>
> _______________________________________________
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to