[rust-dev] the inter-play of struct type impl, traits, type params
Hello, New to Rust. Is there (already) a list for mutual help in the usage of Rust? If not, I guess it may be worth having one, distinct from the dev list, even if the language is a moving target, even for people who rather intend, at terms, to participate in the development. It is in fact even more needed due precisely to the constant change of the lang, to the fact it is mostly unknown, and to the state of the docs. In the meanwhile... see below. Also please pardon wrong ideas or statements: I am presently discovering the language. (Also, english is a foreign lang for me.) As an exercise in learning the language, I am trying various ways to implement sparse arrays (association list, trie, modulo table = hash table w/o hash), the aim being to determine the nr of entries from which it is worth switching to a more complicated version. But I'm far from there yet, stumbling on diverse issues right for the simplest bits of code. Below, I'm talking of the interrelation between struct type declaration impl, type params, and traits. type Key = uint; struct PairListVal { ... } implVal:fmt::Default PairListVal { ... } implVal PairListVal for Iterable { ... } === trait implementation === A first point which I find annoying is the splitting of impl for traits a struct explicitely implements. This means that each time we need to use a generic func for the *usage* of an *instance* of a type, we have to modify this type's *definition* with a new impl section for the corresponding trait(s) required by the generic feature. Or do I misunderstand? (This, even if the the trait is actually implemented, I mean the methods exist, or am I wrong again?) I find that exagerated. Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { === impl itself === As a side note, I also do not understand the purpose of the impl section altogether. I would be happy with: * either the methods inside the struct def: struct PairListVal { fn push (self, key:Key, val:Val) {...} } * or a simple form outside the struct def: fn PairList.push (self, key:Key, val:Val) {...} It is stated somewhere that the impl section nicely separates the fields from the implementation, but this is a question of taste: one could trivially reply this section invents a strange notion of implementation (after all, data fields also are implementation, or rather also definition), and forces to tell apart things that logically fit together. Anyway, it's a question of perspective... I'd vote for the second form above, because it is a kind of intermediate choice. Another advantage is it gives a name to the method (PairList.push). === the meaning of impl for trait sections === Also, I'm not clear about whether impl for trait sections form a kind of namespace. Can there be methods with equal names in diverse such sections (and/or in in th impl section or section not related to traits)? If yes, then it would be in my view a bad idea. Instead, make a kind of standard naming scheme for trait methods. Else, what do such sections mean? === impl type params === As shown above, for a struct type with type params, we have to repeat the said type params in the impl section's headline, and this in fact twice! (I had a hard time with that point.) These repetitions make no sense, in my view, since the type param belongs to the struct type anyway, esp the one after impl; but this one repetition precisely is where to declare traits on type params... see below. I could live with the repetition after the struct type name, as if the type param belonged to the name of the struct type: impl PairListVal { (but as said I would happily forget about impl sections altogether) === traits used === For programmer feedback (read: debug) I need to write out pair-lists (indeed). An issue is that the Val type is a parameter. Since any form of structured output itself requires a format (if only '?') and such formats are traits (! why?), I need to declare this trait (fmt::Default) as belonging to Val --yes, the type param... But where? I had to search for a while before stepping on the right place: as written above right after impl itself. The logical place to make this declaration would be the concerned method, here 'write'. But the said method does not take any Val instance as input, just self: so that there is no place there to declare that Val implements fmt::Default. Or how are we to do it? === universal traits === A distinct but related issue is this trait fmt::Default is supposed universal. Could type params have those universal traits? so that we don't have to declare them. Or better, they are not traits. === everything is a trait === Apparently, in the latest dev of Rust, about everything becomes a trait. This gets crazy ;-). I guess it is wrong in that it over-complicates the language, and there is no limit. Moreover, these layers of
Re: [rust-dev] Abandoning segmented stacks in Rust
If you really need such a small memory footprint for your tasks, I am of the opinion that it would be less error prone (whoops, accidentally used 64 bytes of stack than I should have, now I'm using twice as much memory!) to use an async event loop, like libevent, rather than a task model. It just doesn't seem like it's as worthwhile - if you really need to have that faux synchronous IO, you could use FRP. I'm sure a lot of people would disagree with me, given the general direction rust has been going with tasks (IO, task-local errors, etc.) ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Abandoning segmented stacks in Rust
Segmented stacks aren't the only solution though. If the concern is many tasks that block for a long time, I imagine a mechanism to bundle a bunch of small, dormant stacks into a single page so that the original pages could be released to the OS. If stacks were additionally relocatable (which requires similar machinery as precise moving GC, if I'm not mistaken) then the released pages could be re-used for other tasks or heaps, which would be especially helpful on 32-bit. To address the problem of running out of address space on 32-bit, small-stack tasks could request a single-page stack (plus a guard page), which is enlarged by relocation as needed. Combining these techniques, you might be able to have up to a million small tasks in a 32-bit process. From: Bill Myers bill_my...@outlook.com The advantage of segmented stacks is that blocked tasks only take up as much memory as they actually need to store state, so that for instance a network server can use a task for each connection, and still only use, say, 64 bytes per connection if that's possible instead of the number of stack pages that got allocated for previous computation (assuming an extreme version that allocates a stack segment on every call). However, there is another approach that can replace segmented stacks for that purpose, namely having the compiler automatically transform blocking functions to instead return a future (with limited lifetime). This means that segmented allocation only happens for functions that indirectly perform I/O and only allocates the exact amount of memory needed to retain state that must persistent across the blocking I/O operation, while other functions execute normally using traditional stacks. The simplest example of this feature is async/await in C# 5, and Scala has a delimited continuation passing transformation that can be used to do the same thing. Has this been considered for Rust? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] the inter-play of struct type impl, traits, type params
On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. * either the methods inside the struct def: struct PairListVal { fn push (self, key:Key, val:Val) {...} } That seems somewhat ad-hoc IMHO, because you can implement traits on types that aren't structs. * or a simple form outside the struct def: fn PairList.push (self, key:Key, val:Val) {...} It is stated somewhere that the impl section nicely separates the fields from the implementation, but this is a question of taste: one could trivially reply this section invents a strange notion of implementation (after all, data fields also are implementation, or rather also definition), and forces to tell apart things that logically fit together. Anyway, it's a question of perspective... I'd vote for the second form above, because it is a kind of intermediate choice. Another advantage is it gives a name to the method (PairList.push). We considered the second form, but rejected it, because if you had generic type parameters you'd have to repeat them over and over. Also, I'm not clear about whether impl for trait sections form a kind of namespace. Can there be methods with equal names in diverse such sections (and/or in in th impl section or section not related to traits)? If yes, then it would be in my view a bad idea. Instead, make a kind of standard naming scheme for trait methods. Else, what do such sections mean? I'm not sure what this means. As shown above, for a struct type with type params, we have to repeat the said type params in the impl section's headline, and this in fact twice! (I had a hard time with that point.) These repetitions make no sense, in my view, since the type param belongs to the struct type anyway, esp the one after impl; but this one repetition precisely is where to declare traits on type params... see below. I could live with the repetition after the struct type name, as if the type param belonged to the name of the struct type: impl PairListVal { (but as said I would happily forget about impl sections altogether) There's a big difference between: implT SomeTrait for FooT { ... } And: impl SomeTrait for Fooint { ... } We want to be able to tell the difference between the two at parse time. There is certainly a way to use traits in an opposite manner: they represent functionalities of which we may know they are available when we use other features or types of this or that kind. Currently, traits act conversely, as a constant barrier to usage and expression. With traits as they are (used), it looks like everything is forbidden by default, and we have to discover and explicitely authorise any single bit of functionality. Rust doesn't have C++-style ad-hoc templates; instead it requires that you use concepts in all circumstances. There are two reasons for this: (a) to fix the error message problems of C++ templates; (b) to allow functionality to be added to existing types and used in templates without the C++ *argument-dependent lookup*. Given that C++ is heavily moving toward concepts because the error message problems at least seem intractable without them, I'm personally pretty happy with the decision that we made. It requires more organization up front, but the user experience of the language becomes so much nicer. I love it that a range of prelude features are available in standard. A tiny improvement would be that we don't even need to prefix everything with std::. (In the code above, I had to declare use std:fmt). Instead, just as every prelude feature is available as if we had explicitely imported it, make every (sub)module of std pretend to be at the root of the library. Meaning, whenever the language does not find x::y, have it search std:x:y. This would be particularly appreciated for traits which, as it seems, we'll have to deal with *very* much. We used to do this, but we stopped. The reasons have to do with making imports tractable and the fact that we didn't want to wire std into the compiler more than it needs to be. Now, have a look at [http://static.rust-lang.org/doc/0.8/std/vec.html]: it is by me a webpage long of 50 screens (I use rather big fonts, right); I did not count the functions, but there may be more than 200 probably. And this is nearly a plain list, most funcs are undocumented or just with a short sentence. Actually, most of them seem redondant, there are tons of versions of the same features, depending on various variants. The `vec` module does need to be cleaned up, agreed. I guess much of this mess is due to a kind of over-engineering related in part to traits, or to their current usage in Rust. It seems the combination of traits, and their combinations with other sources of complication such as type params, add barriers to usage and expression. I would caution against drawing too much of a
Re: [rust-dev] Abandoning segmented stacks in Rust
On 11/5/13 8:32 AM, David Piepgrass wrote: Segmented stacks aren't the only solution though. If the concern is many tasks that block for a long time, I imagine a mechanism to bundle a bunch of small, dormant stacks into a single page so that the original pages could be released to the OS. If stacks were additionally relocatable (which requires similar machinery as precise moving GC, if I'm not mistaken) This is correct. It's conceivable (although I can't make any promises) that if and when LLVM supports this, we could experiment with doing what Go does. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] the inter-play of struct type impl, traits, type params
On 11/05/2013 06:17 PM, Patrick Walton wrote: On 11/5/13 2:44 AM, spir wrote: That you very much for this complete answer, Patrick. Things are clearer. Denis ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] the inter-play of struct type impl, traits, type params
On Tue, Nov 5, 2013 at 9:17 AM, Patrick Walton pcwal...@mozilla.com wrote: On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. Isn't another effect of this is the ability to monkey-patch structs to implement extra methods or traits? E.g. you can later in implement a to_str() method for a type, or implement certain traits, like Clone or Drop. -- Ziad ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] the inter-play of struct type impl, traits, type params
As long as you are the person who owns the type, yeah, but I suspect that's not what you mean. Coherence requires that you only implement traits for types if you own either the trait or the type (or both). You can't implement a 3rd party trait for a 3rd party type, since then there could be multiple such implementations for a given (trait, type) pair, and coherence would be broken. On Tue, Nov 5, 2013 at 2:28 PM, Ziad Hatahet hata...@gmail.com wrote: On Tue, Nov 5, 2013 at 9:17 AM, Patrick Walton pcwal...@mozilla.comwrote: On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. Isn't another effect of this is the ability to monkey-patch structs to implement extra methods or traits? E.g. you can later in implement a to_str() method for a type, or implement certain traits, like Clone or Drop. -- Ziad ___ 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
Re: [rust-dev] Abandoning segmented stacks in Rust
On 11/04/2013 07:50 PM, Bill Myers wrote: The advantage of segmented stacks is that blocked tasks only take up as much memory as they actually need to store state, so that for instance a network server can use a task for each connection, and still only use, say, 64 bytes per connection if that's possible instead of the number of stack pages that got allocated for previous computation (assuming an extreme version that allocates a stack segment on every call). In practice there are a number of other limitations that would ever prevent Rust from reducing a stack segment to 64 bytes. Rust segmented stacks still needed a large 'red zone' for running various bits of code, the most problematic being the dynamic linker. On Linux the dynamic linker needs about 2k of space to resolve symbols, on Mac much more. There are ways to work around this by writing our own dynamic linker or disallowing it, but there are significant obstacles to making the stack as small as one might want. We had the Linux minimum stack down to ~3k (1k for Rust code + *2k* red zone). With mmapped stacks we are always free to unmap pages that aren't in use, saving space. However, there is another approach that can replace segmented stacks for that purpose, namely having the compiler automatically transform blocking functions to instead return a future (with limited lifetime). This means that segmented allocation only happens for functions that indirectly perform I/O and only allocates the exact amount of memory needed to retain state that must persistent across the blocking I/O operation, while other functions execute normally using traditional stacks. The simplest example of this feature is async/await in C# 5, and Scala has a delimited continuation passing transformation that can be used to do the same thing. Has this been considered for Rust? Aren't these futures fullfilled by some kind of task abstraction that runs on a thread pool or something? Do these tasks not have their own stacks? I have thought about C#'s async/await feature but decided it was more or less equivalent to tasks. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Abandoning segmented stacks in Rust
C# Async/Await is distinct from segmented stacks because they store the enclosed variables as heap objects rather than on the stack; that means it goes through the same malloc/garbage collector as any other heap objects you create. It's purely a compiler fiction to let you write code that 'looks' like it's stack allocating variables. On Tue, Nov 5, 2013 at 1:42 PM, Brian Anderson bander...@mozilla.comwrote: On 11/04/2013 09:21 PM, Oren Ben-Kiki wrote: Note that as memory becomes cheaper and larger there will be more pressure on 64-bit OS-es to switch to large pages; the number of pages needed to map several GBs of memory today is already getting out of hand, causing TLB misses to become a performance issue in some cases - imagine a system with 0.xTBs of memory and it becomes ludicrous. So playing tricks with MMU and lazy page loading may not work as well as it does with today's the small 4K page size. Of course, Rust is hardly the only platform that would be affected :-) and ideally, it would be possible to have more flexibility than today in choosing which page sizes are used where in the program's address space... but it remains to be seen how exactly this would play out. Just a point to keep in mind... Thanks! That is an interesting point that I hadn't thought about. ___ 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
Re: [rust-dev] Abandoning segmented stacks in Rust
On Tue, Nov 5, 2013 at 4:40 PM, Brian Anderson bander...@mozilla.com wrote: On 11/04/2013 07:50 PM, Bill Myers wrote: The advantage of segmented stacks is that blocked tasks only take up as much memory as they actually need to store state, so that for instance a network server can use a task for each connection, and still only use, say, 64 bytes per connection if that's possible instead of the number of stack pages that got allocated for previous computation (assuming an extreme version that allocates a stack segment on every call). In practice there are a number of other limitations that would ever prevent Rust from reducing a stack segment to 64 bytes. Rust segmented stacks still needed a large 'red zone' for running various bits of code, the most problematic being the dynamic linker. Plus the sysv amd64 ABI requires a 128 byte red zone under the stack anyway. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] the inter-play of struct type impl, traits, type params
The following seems to work: trait Double { fn double(self) - Self; } impl Double for int { fn double(self) - int { *self * 2 } } fn main() { let x = 2; println!({}, x.double()); // prints 4 } -- Ziad On Tue, Nov 5, 2013 at 1:29 PM, Steven Blenkinsop steven...@gmail.comwrote: As long as you are the person who owns the type, yeah, but I suspect that's not what you mean. Coherence requires that you only implement traits for types if you own either the trait or the type (or both). You can't implement a 3rd party trait for a 3rd party type, since then there could be multiple such implementations for a given (trait, type) pair, and coherence would be broken. On Tue, Nov 5, 2013 at 2:28 PM, Ziad Hatahet hata...@gmail.com wrote: On Tue, Nov 5, 2013 at 9:17 AM, Patrick Walton pcwal...@mozilla.comwrote: On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. Isn't another effect of this is the ability to monkey-patch structs to implement extra methods or traits? E.g. you can later in implement a to_str() method for a type, or implement certain traits, like Clone or Drop. -- Ziad ___ 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
Re: [rust-dev] the inter-play of struct type impl, traits, type params
Here you own the trait Double. Doesn't work if you were trying to implement a trait you hadn't just defined. The specific examples you mentioned were Clone and Drop, so that wouldn't work. On Tuesday, November 5, 2013, Ziad Hatahet wrote: The following seems to work: trait Double { fn double(self) - Self; } impl Double for int { fn double(self) - int { *self * 2 } } fn main() { let x = 2; println!({}, x.double()); // prints 4 } -- Ziad On Tue, Nov 5, 2013 at 1:29 PM, Steven Blenkinsop steven...@gmail.comjavascript:_e({}, 'cvml', 'steven...@gmail.com'); wrote: As long as you are the person who owns the type, yeah, but I suspect that's not what you mean. Coherence requires that you only implement traits for types if you own either the trait or the type (or both). You can't implement a 3rd party trait for a 3rd party type, since then there could be multiple such implementations for a given (trait, type) pair, and coherence would be broken. On Tue, Nov 5, 2013 at 2:28 PM, Ziad Hatahet hata...@gmail.comjavascript:_e({}, 'cvml', 'hata...@gmail.com'); wrote: On Tue, Nov 5, 2013 at 9:17 AM, Patrick Walton pcwal...@mozilla.comjavascript:_e({}, 'cvml', 'pcwal...@mozilla.com'); wrote: On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. Isn't another effect of this is the ability to monkey-patch structs to implement extra methods or traits? E.g. you can later in implement a to_str() method for a type, or implement certain traits, like Clone or Drop. -- Ziad ___ Rust-dev mailing list Rust-dev@mozilla.org javascript:_e({}, 'cvml', '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
Re: [rust-dev] the inter-play of struct type impl, traits, type params
Gotcha. But it is still pretty flexible in that you are not bound to the impls that were originally defined on the type (e.g. like C++/Java where the list of interfaces implemented by a class are fixed). This relieves one form writing wrapper classes in order for certain structs to adhere to particular interfaces. -- Ziad On Tue, Nov 5, 2013 at 8:25 PM, Steven Blenkinsop steven...@gmail.comwrote: Here you own the trait Double. Doesn't work if you were trying to implement a trait you hadn't just defined. The specific examples you mentioned were Clone and Drop, so that wouldn't work. On Tuesday, November 5, 2013, Ziad Hatahet wrote: The following seems to work: trait Double { fn double(self) - Self; } impl Double for int { fn double(self) - int { *self * 2 } } fn main() { let x = 2; println!({}, x.double()); // prints 4 } -- Ziad On Tue, Nov 5, 2013 at 1:29 PM, Steven Blenkinsop steven...@gmail.comwrote: As long as you are the person who owns the type, yeah, but I suspect that's not what you mean. Coherence requires that you only implement traits for types if you own either the trait or the type (or both). You can't implement a 3rd party trait for a 3rd party type, since then there could be multiple such implementations for a given (trait, type) pair, and coherence would be broken. On Tue, Nov 5, 2013 at 2:28 PM, Ziad Hatahet hata...@gmail.com wrote: On Tue, Nov 5, 2013 at 9:17 AM, Patrick Walton pcwal...@mozilla.comwrote: On 11/5/13 2:44 AM, spir wrote: Why not just add a declaration of the trait at the top of the struct type def? struct PairListVal : Iterable { You can implement traits on types that aren't structs. Isn't another effect of this is the ability to monkey-patch structs to implement extra methods or traits? E.g. you can later in implement a to_str() method for a type, or implement certain traits, like Clone or Drop. -- Ziad ___ 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
[rust-dev] struct def
I can write this: struct Points {xs:~[uint], ys:~[uint]} fn main () { let mut ps = Points{xs:~[1u], ys:~[1u]}; ... } But I cannot write that: struct PointsT {xs:~[T], ys:~[T]} fn main () { let mut ps = Pointsuint{xs:~[1u], ys:~[1u]}; ... } In the second case, I get the error: sparse_array.rs:106:31: 106:32 error: expected one of `; }` but found `:` sparse_array.rs:106let mut ps = Pointsuint{xs:~[1u], ys:~[1u]}; ^ Sorry to bother you with that, I find myself unable to find the right syntactic schema (and could not find any example in any doc online). I'm blocked, stupidly. spir@ospir:~$ rust -v rust 0.8 host: x86_64-unknown-linux-gnu Also, I have a general problem with writing struct instances with the type apart; meaning, without any type param, I get the same error: struct Points {xs:~[uint], ys:~[uint]} fn main () { let mut ps : Points = {xs:~[1], ys:~[1]}; ... } == parse_array.rs:106:28: 106:29 error: expected one of `; }` but found `:` sparse_array.rs:106let mut ps : Points = {xs:~[1], ys:~[1]}; ^ More generally, I don't know why there are 2 syntactic schemas to define vars. I would be happy with the latter alone (despite the additional pair of spaces) since it is more coherent and more general in allowing temporalily uninitialised declarations. Denis ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev