Hacker News new | past | comments | ask | show | jobs | submit login

> The Rust community seems to be populated entirely by human beings.

:D <3

Regarding your borrow checker example, note that your code is now prone to blowing up if `step` is modified too much. You have created the necessity of an invariant (step should not pop out of the vector) which may be broken by later cleverness.

See http://manishearth.github.io/blog/2015/05/17/the-problem-wit... for more details.

Note that in this specific case you could just use `&str` over `&String` everywhere and push "some new thing" directly; `&String` is a double-pointer, whereas &str is a fat pointer.

> Nor am I totally sure what the tradeoffs are between having a self argument and not.

It's not a tradeoff thing; it's a "do I want a static method, or a member method" thing.

> Some kinds of constraints cannot be used in where clauses, so I believe the former is strictly more powerful.

Actually where clauses are much more powerful. With the type constraints stuff like `A: Foo` works but not `Vec<A>: Foo`, but the latter is allowed in where clauses.

Overall, loved reading this post! It identified some areas of diagnostics that we can try to improve (I'm very interested in fixing diagnostics), and is a pretty accurate picture of the language :)




> Note that in this specific case you could just use `&str` over `&String` everywhere

It's an awkwardly construed example. Here is the actual code - https://gist.github.com/jamii/ae46e8e0c9757330e9ea . There the borrow makes more sense since the value is being created by calling a function on the current solver state.

I've edited the post to include a solution that was suggested in the reddit discussion - replace &'a Value with Cow<'a, Value>.

> It's not a tradeoff thing; it's a "do I want a static method, or a member method" thing.

> Actually where clauses are much more powerful.

I really don't understand this part of the language yet, but...

I linked to an active rfc about constraints that cannot currently be expressed in where clauses (https://github.com/rust-lang/rfcs/blob/master/text/0135-wher...), including the example you gave.

In cases where a function takes two arguments it isn't always obvious which argument should be self. If that affects whether constraints end up being A: Foo<B> or B: Foo<A> or `where Foo<A,B>` it seems like one could run up against those limitations?


I think you may have misread that RFC. With my emphasis:

> Here is a list of limitations with the current bounds syntax that are overcome with the where syntax:

The list is of the limitations of normal bounds, not the limitations of where clauses. This was actually the RFC that added where clauses, and that list was the rationale for doing so.


Oh... well that's embarrassing.

So, I half-remembered the actual problem I ran into and found something that half-looked like it mentioned it. Not my finest hour :S

I dug up the IRC exchange for the problem I actually ran into:

    jamii
    How do I write the type of a byte iterator:
    fn next_path<N: Iterator>(nibbles: &mut N) -> u32 where <N as Iterator>::Item = u8 {
    That gives me 'equality constraints are not yet supported'
    FreeFull
    jamii: <N: Iterator<Item = u8>>
Equality constraints seem to still be unimplemented ( https://github.com/rust-lang/rust/pull/22074) but I can write this instead

    where N : Iterator<Item=u8>
So that whole section of the post is incorrect. I've removed it and linked to this discussion instead.


I'm the author of the above pull request (as well as other pieces of where clauses), unfortunately there are some internals that need refactoring before we can complete equality constraints, and they weren't the highest priority before 1.0 especially since you can encode a trait that acts in the same way. I'm starting at Mozilla for the summer next week and hope to fix a couple of the outstanding issues around them and associated types.


> There the borrow makes more sense since the value is being created by calling a function on the current solver state.

Agreed. And yes, a Cow is better in this case though of course there's a slight runtime cost. In such a case I would heavily document the new invariants with warning comments though :P

> I linked to an active rfc about constraints that cannot currently be expressed in where clauses

Those are the constraints that cannot currently be expressed in bounds, not where clauses. Bounds are the stuff within <>, where clauses were the new thing proposed to supplement them.

> In cases where a function takes two arguments it isn't always obvious which argument should be self.

I'm not sure why you seem to be troubled by self (here and in the post). It might be due to you looking at method dispatch as a sugar for function calls -- it sort of is (because of UFCS), but it really isn't.

The second argument can never be self. It's always the first. (nitpick: functions can't have self args, methods can, but that's just terminology).

For direct impls, you do a method without `self` when you wish it to be a static method. Eg `impl Foo {fn x(){}}` will be a method called as `Foo::x()` and will work independently of any instance of the type. `impl Foo{fn x(self){}}` needs to be called as `foo.x()`, where `foo` is a `Foo`. Here, the method is able to access the state of `foo`.

The behavior so far is the same for most languages like python or java (java has implicit self and uses `static` to say "no self").

Now, traits just let you provide a way to classify objects based on what methods -- both self and nonself/static -- they have. So, a trait `Cloneable` would be `trait Cloneable { fn clone(&self) -> Self}`, and its implementation would be `impl Cloneable for Foo {fn clone(&self) -> Foo {....}}`. This would mean that "given a `Foo`, I should be able to get another `Foo` out of it by calling `foo.clone()`". On the other hand, sometimes you want to run an operation on the type itself. I.e. `trait TypeName {fn type_name() -> &'static str}` and is implemented as `impl TypeName for Foo {fn type_name() -> &'static str {"Foo"}}`. In this case, it's no longer "given a `Foo`, do X", it's "given a type that implements TypeName, do X".

Generally nonself methods on traits don't make much sense unless you're doing type gymnastics (in fact, Java doesn't even allow static methods in interfaces). For example, they're used in hyper for dynamic dispatch on strongly typed headers (http://hyper.rs/hyper/hyper/header/trait.Header.html)

Also, `where Foo<A,B>` doesn't make sense, where clauses need a colon; `where Something: SomethingElse`.


> Those are the constraints that cannot currently be expressed in bounds, not where clauses.

Yeah, I got that whole thing totally wrong. I've removed that part of the post and linked to this discussion instead. Thankyou for de-confusing me :)

> The second argument can never be self. It's always the first.

I think we are talking past each other on this point. I'm thinking of the design-time choice between eg:

    trait Observe<Observee> {
      fn observe(self, observee: Observee);
    }

    trait Observe<Observer> {
      fn observe(self, observer: Observer);
    }

    trait Observe<Observer, Observee> {
      fn observe(observer: Observer, observee: Observee);
    }
Analogous to the typical OO problem where it's not clear which class a method should belong too.

Because I mistakenly believed that where clauses are less powerful I thought that the above choice had additional significance beyond code organisation, because it would affect the kind of constraints I could write. But they aren't so it doesn't matter :)


Oh, I get it now.

Yeah, any of these choices will work^. Well, the last choice shouldn't be a trait, really, just a standalone function.

Associated types would also help simplify this (note, if you have a trait Foo<A>, the trait can be implemented multiple times on the same type, with different A. If you have a trait Foo with associated type A, only one implementation will be allowed, the associated type is a property of the implementation)

^ In more complicated situations coherence may disallow one or more of those choices.


> the trait can be implemented multiple times on the same type, with different A

That was the intention - a function that dispatches on the type of both arguments (it probably shows that I secretly think of traits as typeclasses). I'm still trying to figure out what the implications of the above choices are.

> In more complicated situations coherence may disallow one or more of those choices.

I hadn't thought of that. If the trait is in another crate, does coherence require that the self type is in this crate or that all the types are in this crate? How do the coherence rules work if I don't have self type?


Traits are typeclasses :)

Uh, the coherence rules are complicated and I forgot them. It's a mixture of where the impl, type, trait, and type parameters are.


The general the coherence rules stop you from defining an implementation that could be possibly defined elsewhere.

The important take away implications are: - you can not implement a trait defined in another crate for a type defined in another crate, unless the trait is parametrized by a type from the current crate - you can't define overlapping implementations


So the coherence rules don't treat self specially? That would be good to know.


Is the Rust community really made of humans? I always thought it consisted mostly of crustaceans.


I think the term I've seen is Rustaceans :)


Crustaceans are those coming to the language from C.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: