Re: [rust-dev] Porting some nesC features to rust?
If I get it right, calls to traits are resolved in runtime (so, traits are kind of similar to C++ virtual methods). What I'm proposing here is a compile-time approach. Let's say we have the following trait: pub trait LCD { fn line(mut self, x0_b: i32, y0_b: i32, x1: i32, y1: i32, color: u8); fn rect(mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u8); fn fillrect(mut self, x0_b: i32, y0_b: i32, x1_b: i32, y1_b: i32, color: u8); fn putc(mut self, value: char); fn puts(mut self, s: str); fn flush(self); fn clear(mut self); } which defined a LED screen. There are two structs implementing it: C12832 and ILI9341 (two different lcd controllers). So I want my app to print hello world on lcd, I write the following code: let mut lcd = lcd_c12832::C12832::new(spi); let mut l: mut lcd::LCD = lcd as mut lcd::LCD; l.puts(hello, world); Which results in a runtime dispatch, a slower and bigger code than the one I'd have without a trait. A second problem is there is no easy way to write unified code that supports both the lcds based on passed in --cfg, as I can't apply #[cfg(lcd_c12832)] to a chunk of code in fn, and it's kind of problematic to return a LCD out from it given that there is no heap and no analog of placement new from C++. Proposed binding concept solves those two problems: #[cfg(lcd_c12832)] let Binding: binding { let lcd: lcd_c12832::C12832; let main: Main; bind main.lcd = lcd; } at this point of time compiler can be sure about what struct is implementing LCD trait for main.lcd and can bind the function bodies as compile time, inlining them if applicable. This also might be something that is already implemented, please advice. The goal here is to minimise runtime code being executed and its size. On Mon, Mar 31, 2014 at 3:06 PM, Daniel Micay danielmi...@gmail.com wrote: I'm not really sure exactly what it being proposed here. Rust's generic types and functions are already entirely expanded at compile-time. You *can* use traits as objects for dynamic dispatch, but it's not how they're used in the vast majority of cases. -- Sincerely, Vladimir Farcaller Pouzanov http://farcaller.net/ ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Porting some nesC features to rust?
On 02/04/14 06:25 AM, Vladimir Pouzanov wrote: If I get it right, calls to traits are resolved in runtime (so, traits are kind of similar to C++ virtual methods). All method calls on regular types are resolved via static dispatch, whether or not they come from a trait. For example, consider a generic function like the following: fn minT: TotalOrd(a: T, b: T) - T { if a b { a } else { b } } This function performs a *static* call of the `lt` method defined on the `Ord` trait that `TotalOrd` inherits from. Generics are fully expanded at compile-time just as C++ templates are. Rust also allows using traits as boxed objects, but this is an entirely transparent choice. They're almost always used for static dispatch via trait bounds on generics, or simply outside of generics. What I'm proposing here is a compile-time approach. Let's say we have the following trait: pub trait LCD { fn line(mut self, x0_b: i32, y0_b: i32, x1: i32, y1: i32, color: u8); fn rect(mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u8); fn fillrect(mut self, x0_b: i32, y0_b: i32, x1_b: i32, y1_b: i32, color: u8); fn putc(mut self, value: char); fn puts(mut self, s: str); fn flush(self); fn clear(mut self); } which defined a LED screen. There are two structs implementing it: C12832 and ILI9341 (two different lcd controllers). So I want my app to print hello world on lcd, I write the following code: let mut lcd = lcd_c12832::C12832::new(spi); let mut l: mut lcd::LCD = lcd as mut lcd::LCD; l.puts(hello, world); Which results in a runtime dispatch, a slower and bigger code than the one I'd have without a trait. You can call methods defined on a trait without boxing the object as a trait object. The ability to perform dynamic dispatch via a trait object is totally optional. The methods can also be called directly, including inside a generic function by specifying the trait as a type parameter bound. You can simply call the `puts` method directly on the `lcd` object without a cast. A second problem is there is no easy way to write unified code that supports both the lcds based on passed in --cfg, as I can't apply #[cfg(lcd_c12832)] to a chunk of code in fn, and it's kind of problematic to return a LCD out from it given that there is no heap and no analog of placement new from C++. Rust supports generic functions, and you can write code supporting both types by making it generic. The choice between static dispatch and dynamic dispatch is entirely up to you in the current system. Proposed binding concept solves those two problems: #[cfg(lcd_c12832)] let Binding: binding { let lcd: lcd_c12832::C12832; let main: Main; bind main.lcd = lcd; } at this point of time compiler can be sure about what struct is implementing LCD trait for main.lcd and can bind the function bodies as compile time, inlining them if applicable. This also might be something that is already implemented, please advice. The goal here is to minimise runtime code being executed and its size. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Reminder: ~[T] is not going away
I've noticed recently that there seems to be a bit of confusion about the fate of ~[T] with an impending implementation of DST on the horizon. This has been accompanied with a number of pull requests to completely remove many uses of ~[T] throughout the standard distribution. I'd like to take some time to straighten out what's going on with VecT and ~[T]. # VecT In a post-DST world, VecT will be the vector builder type. It will be the only type for building up a block of contiguous elements. This type exists today, and lives inside of std::vec. Today, you cannot index VecT, but this will be enabled in the future once the indexing traits are fleshed out. This type will otherwise largely not change from what it is today. It will continue to occupy three words in memory, and continue to have the same runtime semantics. # ~[T] The type ~[T] will still exist in a post-DST, but its representation will change. Today, a value of type ~[T] is one word (I'll elide the details of this for now). After DST is implemented, ~[T] will be a two-word value of the length and a pointer to an array (similarly to what slices are today). The ~[T] type will continue to have move semantics, and you can borrow it to [T] as usual. The major difference between today's ~[T] type and a post-DST ~[T] is that the push() method will be removed. There is no knowledge of a capacity in the representation of a ~[T] value, so a push could not be supported at all. In theory a pop() can be efficiently supported, but it will likely not be implemented at first. # [T] As part of DST, the type grammar will start accepting [T] as a possible substitute for type parameters. This basically means that if your type parameters is T, then [U] can satisfy the type parameter. While possible, I imagine that it will be rare for this to appear in apis. This is an unsized type, which means that it's more limited what you can do with it than you can with a sized type. The full details of [T] will become apparent once DST is implemented, but it's safe to say that APIs and usage should rarely have to deal with this type, and it will likely be mostly transparent. # Converting between VecT and ~[T] Conversions between these two types will be provided, and the default implementations will be free. Converting from VecT to ~[T] will be simply forgetting the capacity, and converting from ~[T] to VecT will set the capacity to the length. Helper methods will likely be provided to perform a forceful reallocating shrink when going from VecT to ~[T], but it will not be the default. ## The cost of VecT = ~[T] Some concerns have been brought up that this can in theory be a costly transition under the assumption that this does a reallocation of memory to shrink to the capacity to exactly the length. This will likely not be the default implementation. Some concerns have then been brought up that some allocators require the size of the allocation to be passed to free(), and that this model is incompatible with that flavor of allocator. We believe that this fear can be alleviated with a shrink if necessary method on allocators. The default allocator (backed by the system malloc) would be a no-op because the size to free is not used. Allocators which use the size passed to free would actually perform a reallocation. # Choosing between VecT and ~[T] Primarily, if you need a growable vector, you should use VecT. If you do not need a growable vector, but you're instead just dealing with an array of items, then you should use ~[T]. As a concrete example, I'll take the read_to_end() method on io's Reader trait. This type must use a VecT internally to read data into the vector, but it will return a ~[T] because the contents are conceptually frozen after they have been read. There is no blanket right decision to choose between VecT and ~[T], this will need to be done on a case-by-case basis to evaluate whether apis should take or consume VecT or ~[T]. # Moving Forward In order to implement DST, it is not necessary to remove all usage of ~[T] today. It is necessary to remove all *growable* usage of ~[T], however. All uses of vectors which need growable or shrinkable vectors need to switch to VecT. If a vector does not need to be grown or shrunk, it can remain as ~[T]. Concretely speaking, the next steps forward for ~[T] would entail: * Add a VecT - ~[T] conversion. This will be an expensive conversion today because it requires an allocation (due to the layout of today's ~[T]), but it will not be expensive in the future. * Add a ~[T] - Vec conversion. Like the above step, this will also be expensive, but it will not be so in the future. * Remove the `push` and `pop` families of methods from ~[T] Hopefully that clears up any mystery surrounding what's happening with ~[T] and VecT! If you have any questions, feel free to respond to this email or to join us in IRC. ___ Rust-dev mailing list Rust-dev@mozilla.org
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 11:35 AM, Alex Crichton wrote: I've noticed recently that there seems to be a bit of confusion about the fate of ~[T] with an impending implementation of DST on the horizon. This has been accompanied with a number of pull requests to completely remove many uses of ~[T] throughout the standard distribution. I'd like to take some time to straighten out what's going on with VecT and ~[T]. I think this is a difference of opinion, not confusion. The original pull requests switching `~[T]` to `VecT` were done by pcwalton, and this was with full knowledge of the plans for `~[T]`. # VecT In a post-DST world, VecT will be the vector builder type. It will be the only type for building up a block of contiguous elements. This type exists today, and lives inside of std::vec. Today, you cannot index VecT, but this will be enabled in the future once the indexing traits are fleshed out. It will be Rust's vector (dynamic array) type. I don't think it makes sense to call it a 'builder' any more than it makes sense to call `HashMapK, V` a 'hash table builder'. It makes something simple far more complicated than it needs to be. This type will otherwise largely not change from what it is today. It will continue to occupy three words in memory, and continue to have the same runtime semantics. # ~[T] The type ~[T] will still exist in a post-DST, but its representation will change. Today, a value of type ~[T] is one word (I'll elide the details of this for now). After DST is implemented, ~[T] will be a two-word value of the length and a pointer to an array (similarly to what slices are today). The ~[T] type will continue to have move semantics, and you can borrow it to [T] as usual. The `~[T]` type will exist because `[T]` will exist as a type. It won't be an explicit choice to support having it. Some of us consider it an unfortunate consequence of DST rather than a useful type. The major difference between today's ~[T] type and a post-DST ~[T] is that the push() method will be removed. There is no knowledge of a capacity in the representation of a ~[T] value, so a push could not be supported at all. In theory a pop() can be efficiently supported, but it will likely not be implemented at first. A `pop` or `shift` function is impossible to implement efficiently if allocators require a size to be passed to `free`. # [T] As part of DST, the type grammar will start accepting [T] as a possible substitute for type parameters. This basically means that if your type parameters is T, then [U] can satisfy the type parameter. While possible, I imagine that it will be rare for this to appear in apis. This is an unsized type, which means that it's more limited what you can do with it than you can with a sized type. The full details of [T] will become apparent once DST is implemented, but it's safe to say that APIs and usage should rarely have to deal with this type, and it will likely be mostly transparent. # Converting between VecT and ~[T] Conversions between these two types will be provided, and the default implementations will be free. Converting from VecT to ~[T] will be simply forgetting the capacity, and converting from ~[T] to VecT will set the capacity to the length. Converting from `VecT` to `~[T]` will not be free with an efficient allocation scheme. I don't think Rust will want to be using a legacy `malloc`/`free` style API as the underlying default allocator in the future. I see it only as a temporary measure before a modern allocation model is implemented. Without a size parameter to `free`, an allocator needs to track the size of allocations manually. It increases the memory overhead, along with adding bookkeeping overhead. C++ allocators take a `size` parameter to the `deallocate` function for this reason and I expect Rust will want to do the same. The design of `malloc` and `free` is far from ideal, because the length is either known statically or dynamically in nearly every case. I think leaving out the capacity field of vectors in some cases without dropping the excess capacity is an an insignificant micro-optimization. In contract, passing the length to `free` is quite valuable and will result in a measurable performance win across nearly all Rust code with an allocator taking advantage of it. Helper methods will likely be provided to perform a forceful reallocating shrink when going from VecT to ~[T], but it will not be the default. It has to be the *only* way to do it if Rust is going to be able to switch to an efficient allocation model in the future. The API of `malloc`, `realloc` and `free` is purely a legacy wart and shouldn't drive the design of a new language/library. ## The cost of VecT = ~[T] Some concerns have been brought up that this can in theory be a costly transition under the assumption that this does a reallocation of memory to shrink to the capacity to exactly the length. This will likely not be the
Re: [rust-dev] 0.10 prerelease testing
I compiled from source just yesterday, but everything's been going swimmingly! I just have one comment on 0.10: It seems like println was removed from the prelude. While I can totally appreciate that most people will use println!, which is automatically use-able, it _is_ making my 'hello world' examples significantly more complex, since basically every one of them needs to either import println or use println!({}, foo); I'm not sure if this is a good or bad thing, just wanted to raise that as a possible issue. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] 0.10 prerelease testing
On Wed, Apr 2, 2014 at 1:34 PM, Steve Klabnik st...@steveklabnik.com wrote: I compiled from source just yesterday, but everything's been going swimmingly! I just have one comment on 0.10: It seems like println was removed from the prelude. While I can totally appreciate that most people will use println!, which is automatically use-able, it _is_ making my 'hello world' examples significantly more complex, since basically every one of them needs to either import println or use println!({}, foo); I'm not sure if this is a good or bad thing, just wanted to raise that as a possible issue. It has been raised, as an extension to the macro, that invocation with a single, non-string literal, could expand into `println!({}, $that_arg)` rather than requiring the `{}`. -- http://octayn.net/ ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 4/2/14 9:25 AM, Daniel Micay wrote: On 02/04/14 11:35 AM, Alex Crichton wrote: I've noticed recently that there seems to be a bit of confusion about the fate of ~[T] with an impending implementation of DST on the horizon. This has been accompanied with a number of pull requests to completely remove many uses of ~[T] throughout the standard distribution. I'd like to take some time to straighten out what's going on with VecT and ~[T]. I think this is a difference of opinion, not confusion. The original pull requests switching `~[T]` to `VecT` were done by pcwalton, and this was with full knowledge of the plans for `~[T]`. It was transitionary. I thought that we would have to fully extract `~[T]` from the language before DST would work, but it now seems likely that that won't need to happen. The `~[T]` type will exist because `[T]` will exist as a type. It won't be an explicit choice to support having it. Some of us consider it an unfortunate consequence of DST rather than a useful type. Even if you buy that `~[T]` is useless (which I'm not sure I do), it's no more unfortunate than the fact that the type system allows useless types like `RcRcRcint` is unfortunate. If `~[T]` remains used throughout the libraries, Rust will become noisier than languages like C++ with a unified vector type. The need to convert between `VecT` and `~[T]` would add noise to lots of code, without any adding measurable optimization win. A micro-optimization shouldn't drive the design of the libraries, especially when it will prevent making a significant *macro*-optimization (passing a length to the deallocation function). In practice C++ libraries use their own custom vector types all over the place, so I wouldn't say that Rust is going to be significantly noisier no matter what we do. Interoperability between different libraries is not a strong point of C++. Besides, C++ has this too, with `unique_ptrT[]`. This Stack Overflow answer is actually pretty illuminating: http://stackoverflow.com/questions/16711697/is-there-any-use-for-unique-ptr-with-array I think that length-frozen owned vectors are likely to be surprisingly common. We'll see. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 02:28 PM, Patrick Walton wrote: On 4/2/14 9:25 AM, Daniel Micay wrote: On 02/04/14 11:35 AM, Alex Crichton wrote: I've noticed recently that there seems to be a bit of confusion about the fate of ~[T] with an impending implementation of DST on the horizon. This has been accompanied with a number of pull requests to completely remove many uses of ~[T] throughout the standard distribution. I'd like to take some time to straighten out what's going on with VecT and ~[T]. I think this is a difference of opinion, not confusion. The original pull requests switching `~[T]` to `VecT` were done by pcwalton, and this was with full knowledge of the plans for `~[T]`. It was transitionary. I thought that we would have to fully extract `~[T]` from the language before DST would work, but it now seems likely that that won't need to happen. The `~[T]` type will exist because `[T]` will exist as a type. It won't be an explicit choice to support having it. Some of us consider it an unfortunate consequence of DST rather than a useful type. Even if you buy that `~[T]` is useless (which I'm not sure I do), it's no more unfortunate than the fact that the type system allows useless types like `RcRcRcint` is unfortunate. No one is proposing that we use `RcRcRcint` in the standard library. Using `~[T]` instead of migrating to `VecT` means there will be conversion noise where there was not going to be conversion noise before. If `~[T]` remains used throughout the libraries, Rust will become noisier than languages like C++ with a unified vector type. The need to convert between `VecT` and `~[T]` would add noise to lots of code, without any adding measurable optimization win. A micro-optimization shouldn't drive the design of the libraries, especially when it will prevent making a significant *macro*-optimization (passing a length to the deallocation function). In practice C++ libraries use their own custom vector types all over the place, so I wouldn't say that Rust is going to be significantly noisier no matter what we do. Interoperability between different libraries is not a strong point of C++. Besides, C++ has this too, with `unique_ptrT[]`. This Stack Overflow answer is actually pretty illuminating: `std::unique_ptr[T]` is useful because lots of legacy code uses the new[]/delete[] memory allocations. Unique pointers also take a custom deleter parameter, because they're usable for managing stuff like files, etc. in C++. I think that length-frozen owned vectors are likely to be surprisingly common. We'll see. They'll certainly be common if the standard library forces many conversions to and from `VecT`... It should not be stated that this conversion is free though, because it only remains free as long as you're using a legacy allocation API like `malloc`. It's also not free in terms of language complexity - people are going to wonder when they should use each one, and I know I'm certainly going to be telling people to use `VecT` almost everywhere. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On Wed, Apr 2, 2014 at 12:25 PM, Daniel Micay danielmi...@gmail.com wrote: Without a size parameter to `free`, an allocator needs to track the size of allocations manually. It increases the memory overhead, along with adding bookkeeping overhead. Not by very much... If a chunk's header is stored externally, like tcmalloc and Linux slub, there is virtually no memory overhead at the cost of free involving a quick hash table lookup on the address; if it's stored internally, like jemalloc, the overhead is just possibly some page-size-remainder wastage, and free just masks the pointer. Either way, if chunks are ever going to be freed, you need some kind of header to count free slots. I guess knowing the size would help the fast path for free be really simple and even inlined, since it could just swap a fixed thread-local variable. But is that really worth hanging language features on, one way or the other? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
Passing the size to free is currently in a C++14 proposal [1]. It's pretty useful (makes free no slower, might make it faster) and in most code, the size is available on free. I'm not sure it would should be mandatory, but it's definitely useful. [1] http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html On Wed, Apr 2, 2014 at 3:13 PM, comex com...@gmail.com wrote: On Wed, Apr 2, 2014 at 12:25 PM, Daniel Micay danielmi...@gmail.com wrote: Without a size parameter to `free`, an allocator needs to track the size of allocations manually. It increases the memory overhead, along with adding bookkeeping overhead. Not by very much... If a chunk's header is stored externally, like tcmalloc and Linux slub, there is virtually no memory overhead at the cost of free involving a quick hash table lookup on the address; if it's stored internally, like jemalloc, the overhead is just possibly some page-size-remainder wastage, and free just masks the pointer. Either way, if chunks are ever going to be freed, you need some kind of header to count free slots. I guess knowing the size would help the fast path for free be really simple and even inlined, since it could just swap a fixed thread-local variable. But is that really worth hanging language features on, one way or the other? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev -- Clark. Key ID : 0x78099922 Fingerprint: B292 493C 51AE F3AB D016 DD04 E5E3 C36F 5534 F907 ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 03:18 PM, Clark Gaebel wrote: Passing the size to free is currently in a C++14 proposal [1]. It's pretty useful (makes free no slower, might make it faster) and in most code, the size is available on free. I'm not sure it would should be mandatory, but it's definitely useful. [1] http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html Allocators already do take the size, so it already works for containers, etc. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 03:13 PM, comex wrote: On Wed, Apr 2, 2014 at 12:25 PM, Daniel Micay danielmi...@gmail.com wrote: Without a size parameter to `free`, an allocator needs to track the size of allocations manually. It increases the memory overhead, along with adding bookkeeping overhead. Not by very much... If a chunk's header is stored externally, like tcmalloc and Linux slub, there is virtually no memory overhead at the cost of free involving a quick hash table lookup on the address; if it's stored internally, like jemalloc, the overhead is just possibly some page-size-remainder wastage, and free just masks the pointer. Either way, if chunks are ever going to be freed, you need some kind of header to count free slots. You're talking about allocators designed around the limitation of an API. The design no longer needs to make the same compromises if you're going to know the size. The difference between no cache miss and a cache miss is not insignificant... I guess knowing the size would help the fast path for free be really simple and even inlined, since it could just swap a fixed thread-local variable. It's a significant optimization. There's a reason this was included in the C++ allocator design and is being extended to more of the language in C++14. But is that really worth hanging language features on, one way or the other? Is it really worth designing the language around the micro-optimization of leaving off a capacity field? Rust's syntax is verbose enough without needing to convert to and from vector/string builders all the time. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 03:13 PM, comex wrote: On Wed, Apr 2, 2014 at 12:25 PM, Daniel Micay danielmi...@gmail.com wrote: But is that really worth hanging language features on, one way or the other? This also isn't the only optimization lost here. Zero-size allocations will need to be clamped to one if passing a size to free isn't required. Why? Rust uses a non-nullable pointer optimization, where Option~T and similar enums can be stored without a tag. This optimization should also be extended to types like slices in the future. It applies to the current `~[T]` but would need to be adapted to a new representation. It's important to avoid allocating for a zero-size allocation, in order to save memory for ~Trait with zero-size types and to avoid allocating in zero-size vectors. However, this means that a zero-size allocation needs to be represented as non-null. Rust needs a way of knowing that despite being non-null, there is no allocated capacity. For example, consider a 0-size slice: (0x22, 0) When this is passed to `free`, Rust needs to be sure that a 0-size slice also has a 0-size capacity. In order to do that, shrink_to_fit() needs to happen during VecT - ~[T] conversions. At the moment, Rust is completely broken in this regard. The following expression evaluates to None: Some(~()) I have no sane proposal to fix this beyond passing a size to free. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
At the moment, Rust is completely broken in this regard. The following expression evaluates to None: Some(~()) Ouch, this is a disaster. Is there a bug filed for this? Anyway, I don't get your argument about size to free having anything to do with fixing it (although I agree that size to free is awesome). If you don't care about equality (i.e. the fact that *~() != *~(), but a == a where a = *~()), just return the address of a single private static 1-byte item for any 0-sized allocation. If you DO care about equality, then you will need at least an integer allocation scheme in all cases on 32-bit platforms, and the real costs are the data structures to track that (at least a bit in a bitmask, probably at least 2 bits for an efficient implementation). If you can't use the 1-2GB of kernel address space, then you'll also need to allocate one byte of actual usable address space (but not committed memory). On 64-bit platforms, you generally have at least around 2^60-2^63 bytes of unusable address space, so you can just increment a pointer pointing there for each allocation, at zero cost. Of course the quick and simple fix is to try to call malloc(0) and if it returns NULL, remember that and switch to using malloc(1) instead. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
Clamping `malloc(0)` to `malloc(1)` means that allocations of 0-size types will no longer be free, which is sad. It's very useful to be able to have meet the requirement of having a trait object and avoid any memory allocation if there's no state. The sentinel does work, but adds a branch to *every* free call. It will not optimize out even for cases where the size is fixed at compile time. This isn't a significant issue for the default allocator because it will be complex, but it's a significant issue with a bump/arena allocator, or a simple free list. It's less overhead than not having a size available will be, but why not kill two birds with one stone? signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] 0.10 prerelease testing
I've been worried about this decision too. On 04/02/2014 10:34 AM, Steve Klabnik wrote: I compiled from source just yesterday, but everything's been going swimmingly! I just have one comment on 0.10: It seems like println was removed from the prelude. While I can totally appreciate that most people will use println!, which is automatically use-able, it _is_ making my 'hello world' examples significantly more complex, since basically every one of them needs to either import println or use println!({}, foo); I'm not sure if this is a good or bad thing, just wanted to raise that as a possible issue. ___ 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] 0.10 prerelease testing
On 02/04/2014 18:43, Corey Richardson wrote: On Wed, Apr 2, 2014 at 1:34 PM, Steve Klabnik st...@steveklabnik.com wrote: I compiled from source just yesterday, but everything's been going swimmingly! I just have one comment on 0.10: It seems like println was removed from the prelude. While I can totally appreciate that most people will use println!, which is automatically use-able, it _is_ making my 'hello world' examples significantly more complex, since basically every one of them needs to either import println or use println!({}, foo); I'm not sure if this is a good or bad thing, just wanted to raise that as a possible issue. It has been raised, as an extension to the macro, that invocation with a single, non-string literal, could expand into `println!({}, $that_arg)` rather than requiring the `{}`. This sounds even better than having both println() and println!() (in the prelude) with non-obvious differences. -- Simon Sapin ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
Personally, I'm strongly against doing using ~[] as return values from library functions. Imagine we were in world were we only had VecT and were adding a new type OwnedSliceT that was (pointer, length) like ~[T]. For how many library functions would we say it is sensible to throw away the capacity information before returning? I don't think anything in libstd etc. would have a strong 'yes' answer to this question. Specifically, I don't see any concrete positives to doing this for library functions other than lets keep using ~[T] and ~[T] [T] having the same in-memory representation (covered below). Under any scheme I can think of, there are negatives: 1. without calling shrink_to_fit in the conversion, we lose the ability to have sized deallocations (covered by others in this thread) 2. if we do call it, then anything returning a ~[T] after building it with a VecT is unavoidably slower 3. either way, you're throwing away (the knowledge of) any extra capacity that was allocated, so if someone wishes to continue extending the slice returned by e.g. `foo`, then `let v = foo().into_vec(); v.push(1)` will always require a realloc. (And for library functions, we shouldn't be dictating how people use the return values.) 4. it adds two vector-like types that someone needs to think about: in the common case the benefits of ~[] (one word smaller) are completely useless, it's really only mostly-immutable heavily-nested data types with a lot of vectors like Rust's AST where it helps[1]. I.e. almost all situations are fine (or better) with a Vec. 5. how will the built-in ~[] type use allocators? (well, I guess this is really how will the built-in ~ type use allocators?, but that question still needs answering[2].) On the representation of ~[T] and [T] being the same: this means that theoretically a ~[T] in covariant(?) position can be coerced to a [T], e.g. Vec~[T] - Vec[T]. However, this only really matters for functions returning many nested slices/vectors, e.g. the same Vec example, because pretty much anything else will be able to write `vec.as_slice()` cheaply. (In the code base, the only things mentioning /~[~[/ now are a few tests and things handling the raw argc/argv, i.e. returning ~[~[u8]].) I don't think this should be a major concern, because I don't see us suddenly growing functions a pile of new functions returning ~[~[T]], and if we do, I would think that they would be better suited to being an iterator (assuming that's possible) over Vec's, and these internal Vec can be then be mapped to ~[T] cheaply before collecting the iterator to a whole new VecVec (or Vec~[]) (assuming a [Vec]/[~[]] is wanted). I'm concerned we are wanting to stick with ~[T] because it's what we currently have, and is familiar; as I said above, I don't see many positives for doing it for library functions. Huon [1]: And even in those cases, it's not a particularly huge gain, e.g. taking *two* words off the old OptVec type by replacing it with a library equivalent to DST's ~[T] only gained about 40MB: http://huonw.github.io/isrustfastyet/mem/#f5357cf,bbf8cdc [2]: The sanest way to support allocators I can think of would be changing `~T` to `UniqT, A=DefaultAlloc`, and then we have `Uniq[T]` which certainly feels less attractive than `~[T]`. On 03/04/14 02:35, Alex Crichton wrote: I've noticed recently that there seems to be a bit of confusion about the fate of ~[T] with an impending implementation of DST on the horizon. This has been accompanied with a number of pull requests to completely remove many uses of ~[T] throughout the standard distribution. I'd like to take some time to straighten out what's going on with VecT and ~[T]. # VecT In a post-DST world, VecT will be the vector builder type. It will be the only type for building up a block of contiguous elements. This type exists today, and lives inside of std::vec. Today, you cannot index VecT, but this will be enabled in the future once the indexing traits are fleshed out. This type will otherwise largely not change from what it is today. It will continue to occupy three words in memory, and continue to have the same runtime semantics. # ~[T] The type ~[T] will still exist in a post-DST, but its representation will change. Today, a value of type ~[T] is one word (I'll elide the details of this for now). After DST is implemented, ~[T] will be a two-word value of the length and a pointer to an array (similarly to what slices are today). The ~[T] type will continue to have move semantics, and you can borrow it to [T] as usual. The major difference between today's ~[T] type and a post-DST ~[T] is that the push() method will be removed. There is no knowledge of a capacity in the representation of a ~[T] value, so a push could not be supported at all. In theory a pop() can be efficiently supported, but it will likely not be implemented at first. # [T] As part of DST, the type grammar will start
Re: [rust-dev] Reminder: ~[T] is not going away
On 4/2/14 2:51 PM, Huon Wilson wrote: Specifically, I don't see any concrete positives to doing this for library functions other than lets keep using ~[T] and ~[T] [T] having the same in-memory representation (covered below). Under any scheme I can think of, there are negatives: 1. without calling shrink_to_fit in the conversion, we lose the ability to have sized deallocations (covered by others in this thread) 2. if we do call it, then anything returning a ~[T] after building it with a VecT is unavoidably slower 3. either way, you're throwing away (the knowledge of) any extra capacity that was allocated, so if someone wishes to continue extending the slice returned by e.g. `foo`, then `let v = foo().into_vec(); v.push(1)` will always require a realloc. (And for library functions, we shouldn't be dictating how people use the return values.) 4. it adds two vector-like types that someone needs to think about: in the common case the benefits of ~[] (one word smaller) are completely useless, it's really only mostly-immutable heavily-nested data types with a lot of vectors like Rust's AST where it helps[1]. I.e. almost all situations are fine (or better) with a Vec. 5. how will the built-in ~[] type use allocators? (well, I guess this is really how will the built-in ~ type use allocators?, but that question still needs answering[2].) On the representation of ~[T] and [T] being the same: this means that theoretically a ~[T] in covariant(?) position can be coerced to a [T], e.g. Vec~[T] - Vec[T]. However, this only really matters for functions returning many nested slices/vectors, e.g. the same Vec example, because pretty much anything else will be able to write `vec.as_slice()` cheaply. (In the code base, the only things mentioning /~[~[/ now are a few tests and things handling the raw argc/argv, i.e. returning ~[~[u8]].) I don't think this should be a major concern, because I don't see us suddenly growing functions a pile of new functions returning ~[~[T]], and if we do, I would think that they would be better suited to being an iterator (assuming that's possible) over Vec's, and these internal Vec can be then be mapped to ~[T] cheaply before collecting the iterator to a whole new VecVec (or Vec~[]) (assuming a [Vec]/[~[]] is wanted). I'm concerned we are wanting to stick with ~[T] because it's what we currently have, and is familiar; as I said above, I don't see many positives for doing it for library functions. What about strings? Should we be using `StrBuf` as well? Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 03/04/14 08:54, Patrick Walton wrote: On 4/2/14 2:51 PM, Huon Wilson wrote: Specifically, I don't see any concrete positives to doing this for library functions other than lets keep using ~[T] and ~[T] [T] having the same in-memory representation (covered below). Under any scheme I can think of, there are negatives: 1. without calling shrink_to_fit in the conversion, we lose the ability to have sized deallocations (covered by others in this thread) 2. if we do call it, then anything returning a ~[T] after building it with a VecT is unavoidably slower 3. either way, you're throwing away (the knowledge of) any extra capacity that was allocated, so if someone wishes to continue extending the slice returned by e.g. `foo`, then `let v = foo().into_vec(); v.push(1)` will always require a realloc. (And for library functions, we shouldn't be dictating how people use the return values.) 4. it adds two vector-like types that someone needs to think about: in the common case the benefits of ~[] (one word smaller) are completely useless, it's really only mostly-immutable heavily-nested data types with a lot of vectors like Rust's AST where it helps[1]. I.e. almost all situations are fine (or better) with a Vec. 5. how will the built-in ~[] type use allocators? (well, I guess this is really how will the built-in ~ type use allocators?, but that question still needs answering[2].) On the representation of ~[T] and [T] being the same: this means that theoretically a ~[T] in covariant(?) position can be coerced to a [T], e.g. Vec~[T] - Vec[T]. However, this only really matters for functions returning many nested slices/vectors, e.g. the same Vec example, because pretty much anything else will be able to write `vec.as_slice()` cheaply. (In the code base, the only things mentioning /~[~[/ now are a few tests and things handling the raw argc/argv, i.e. returning ~[~[u8]].) I don't think this should be a major concern, because I don't see us suddenly growing functions a pile of new functions returning ~[~[T]], and if we do, I would think that they would be better suited to being an iterator (assuming that's possible) over Vec's, and these internal Vec can be then be mapped to ~[T] cheaply before collecting the iterator to a whole new VecVec (or Vec~[]) (assuming a [Vec]/[~[]] is wanted). I'm concerned we are wanting to stick with ~[T] because it's what we currently have, and is familiar; as I said above, I don't see many positives for doing it for library functions. What about strings? Should we be using `StrBuf` as well? Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev I don't see why not. The same arguments apply. Huon ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On Wed, Apr 02, 2014 at 04:03:37PM -0400, Daniel Micay wrote: I have no sane proposal to fix this beyond passing a size to free. I don't believe there is a problem with just not using null to represent such pointers (for example, 1 would suffice). This does impose some additional burdens on slice conversion and the like. This conversation has focused on low-level effects, which is important to understand, but I think the bigger question is: how do we WANT the language to look? Is it useful to have a distinct `VecT` and `~[T]` or -- in our ideal world -- would they be the same? I think we can make the interconversion fast for the default allocator, but we should design for the language we want to use. I could go either way on this. In the kind of programs I write, at least, most vectors get built up to a specific length and then stop growing (frequently they stop changing as well, but not always). Sometimes they continue growing. I actually rather like the idea of using `VecT` as a kind of builder and `~[T]` as the end-product. In those cases where the vector continues to grow, of course, I can just keep the `VecT` around. Following this logic, I would imagine that most APIs want to consume and produce `~[T]`, since they consume and produce end products. On the other hand, I could imagine and appreciate an argument that we should just take and produce `VecT`, which gives somewhat more flexibility. In general, Rust takes the philosophy that if you own it, you can mutate it, so why make growing harder than it needs to be? Preferring VecT also means fewer choices, usually a good thing. Perhaps the best thing is to wait a month (or two or three) until DST is more of a reality and then see how we feel. Niko ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On 02/04/14 07:22 PM, Niko Matsakis wrote: On Wed, Apr 02, 2014 at 04:03:37PM -0400, Daniel Micay wrote: I have no sane proposal to fix this beyond passing a size to free. I don't believe there is a problem with just not using null to represent such pointers (for example, 1 would suffice). This does impose some additional burdens on slice conversion and the like. I used a sentinel value in my fix along with providing a guarantee that `free` is never called on zero-size allocation. That's the end of any no-op `VecT` - `~[T]` conversions since it will need to free a zero size allocation. It's not far from just calling `shrink_to_fit`, and allowing for passing a size to `free`. https://github.com/mozilla/rust/pull/13267 I don't think there's any way around without making `~ZeroSizeType` start allocating memory or losing the `OptionNonNullablePointer` optimization otherwise. This conversation has focused on low-level effects, which is important to understand, but I think the bigger question is: how do we WANT the language to look? Is it useful to have a distinct `VecT` and `~[T]` or -- in our ideal world -- would they be the same? I think we can make the interconversion fast for the default allocator, but we should design for the language we want to use. A distinct `~[T]` and `VecT` will make the language more painful to use, so the only point I'm trying to counter is the performance one because it is *is* a valid micro-optimization in some cases. If our default allocation scheme takes advantage of a known size, then it will be faster. I don't think we should keep using a malloc/realloc/free-style API under the hood in the future. I could go either way on this. In the kind of programs I write, at least, most vectors get built up to a specific length and then stop growing (frequently they stop changing as well, but not always). Sometimes they continue growing. I actually rather like the idea of using `VecT` as a kind of builder and `~[T]` as the end-product. In those cases where the vector continues to grow, of course, I can just keep the `VecT` around. Following this logic, I would imagine that most APIs want to consume and produce `~[T]`, since they consume and produce end products. The language needs to be providing a significant safety/correctness guarantee or performance win in exchange for the extra noise and I don't really think it will be in general. There will be use cases for `~[T]` but I don't think they will be common. If an API consumes `~[T]`, it will lose track of capacity the caller may already be able to provide. If it produces `~[T]`, it will lose track of capacity the caller may want to use later on. On the other hand, I could imagine and appreciate an argument that we should just take and produce `VecT`, which gives somewhat more flexibility. In general, Rust takes the philosophy that if you own it, you can mutate it, so why make growing harder than it needs to be? Preferring VecT also means fewer choices, usually a good thing. Perhaps the best thing is to wait a month (or two or three) until DST is more of a reality and then see how we feel. Niko signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Porting some nesC features to rust?
And just in case there is a confusion (as I have noticed others to have), it might help to see a specific example comparing static dispatch with dynamic. // This is a single function for all types implementing the LCD Trait. fn foo(x : LCD) { // x's type is LCD rather than the actual type of the object being passed in x.line(); // dynamic dispatch } // Like C++ templates, this generates a function for each type T that implements LCD. fn fooT : LCD(x : T) { // x's type is T rather than LCD x.line(); // static dispatch based on type T known at compile-time } On Wed, Apr 2, 2014 at 8:32 AM, Daniel Micay danielmi...@gmail.com wrote: On 02/04/14 06:25 AM, Vladimir Pouzanov wrote: If I get it right, calls to traits are resolved in runtime (so, traits are kind of similar to C++ virtual methods). All method calls on regular types are resolved via static dispatch, whether or not they come from a trait. For example, consider a generic function like the following: fn minT: TotalOrd(a: T, b: T) - T { if a b { a } else { b } } This function performs a *static* call of the `lt` method defined on the `Ord` trait that `TotalOrd` inherits from. Generics are fully expanded at compile-time just as C++ templates are. Rust also allows using traits as boxed objects, but this is an entirely transparent choice. They're almost always used for static dispatch via trait bounds on generics, or simply outside of generics. What I'm proposing here is a compile-time approach. Let's say we have the following trait: pub trait LCD { fn line(mut self, x0_b: i32, y0_b: i32, x1: i32, y1: i32, color: u8); fn rect(mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u8); fn fillrect(mut self, x0_b: i32, y0_b: i32, x1_b: i32, y1_b: i32, color: u8); fn putc(mut self, value: char); fn puts(mut self, s: str); fn flush(self); fn clear(mut self); } which defined a LED screen. There are two structs implementing it: C12832 and ILI9341 (two different lcd controllers). So I want my app to print hello world on lcd, I write the following code: let mut lcd = lcd_c12832::C12832::new(spi); let mut l: mut lcd::LCD = lcd as mut lcd::LCD; l.puts(hello, world); Which results in a runtime dispatch, a slower and bigger code than the one I'd have without a trait. You can call methods defined on a trait without boxing the object as a trait object. The ability to perform dynamic dispatch via a trait object is totally optional. The methods can also be called directly, including inside a generic function by specifying the trait as a type parameter bound. You can simply call the `puts` method directly on the `lcd` object without a cast. A second problem is there is no easy way to write unified code that supports both the lcds based on passed in --cfg, as I can't apply #[cfg(lcd_c12832)] to a chunk of code in fn, and it's kind of problematic to return a LCD out from it given that there is no heap and no analog of placement new from C++. Rust supports generic functions, and you can write code supporting both types by making it generic. The choice between static dispatch and dynamic dispatch is entirely up to you in the current system. Proposed binding concept solves those two problems: #[cfg(lcd_c12832)] let Binding: binding { let lcd: lcd_c12832::C12832; let main: Main; bind main.lcd = lcd; } at this point of time compiler can be sure about what struct is implementing LCD trait for main.lcd and can bind the function bodies as compile time, inlining them if applicable. This also might be something that is already implemented, please advice. The goal here is to minimise runtime code being executed and its size. ___ 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] Reminder: ~[T] is not going away
On Apr 2, 2014, at 8:35 AM, Alex Crichton a...@crichton.co wrote: As a concrete example, I'll take the read_to_end() method on io's Reader trait. This type must use a VecT internally to read data into the vector, but it will return a ~[T] because the contents are conceptually frozen after they have been read. This concrete example is great, because it precisely illustrates a major objection I have to returning ~[T]. Reader.read_to_end() internally uses a 64k-byte vector. It reserves 64k bytes, then pushes onto this vector until it hits EOF. Every time it fills up the 64k capacity it reserves another chunk and keeps reading (this, btw, is I think almost certainly unintended behavior and is fixed by #13127, which changes it to always keep 64k of space available for each read rather than potentially requesting smaller and smaller reads). Note that because it uses reserve_at_least() it may actually have more than 64k available. When EOF is reached, this vector is returned to the caller. The problem I have with returning ~[T] here is that both choices for how to deal with this wasted space are terrible: 1. Shrink-to-fit before returning. If I'm going to keep the vector around for a long time this is a good idea, but if I'm just going to process the vector and throw it away, the reallocation was completely unnecessary. 2. Convert to ~[T] without shrinking. The caller has no way to know about the potentially massive amount of wasted space. If I'm going to just process the vector and throw it away that's fine, but if I'm going to keep it around for a while then this is terrible. The only reasonable solution is to return the VecT and let the caller decide if they want to shrink-to-fit or not. -Kevin Ballard ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Reminder: ~[T] is not going away
On Apr 2, 2014, at 3:01 PM, Huon Wilson dbau...@gmail.com wrote: On 03/04/14 08:54, Patrick Walton wrote: What about strings? Should we be using `StrBuf` as well? I don't see why not. The same arguments apply. I agree. I was actually quite surprised to see that the type was named StrBuf, I assumed it was going to be Str just as Vec is not VecBuf. I'm in full agreement with Huon on this matter. The standard libraries should return VecT instead of ~[T] in pretty much every case (the only real exception I can think of is Vec~[T] because of the ability to convert to Vec[T] or [T]] for free). Similarly I think we should be returning StrBuf instead of ~str in all cases. And finally, I think we should just name it Str instead of StrBuf. If developers want to use ~[T] and ~str in their own code, that's fine, but the standard libraries should err on the side of preserving information (e.g. capacity) and providing a consistent experience. If there's one thing I really want to avoid above all else, it's confusing people about whether they should be using ~[T] or VecT, because some standard library code uses one and some code uses the other. -Kevin ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] 0.10 prerelease testing
On Apr 2, 2014, at 2:08 PM, Simon Sapin simon.sa...@exyr.org wrote: On 02/04/2014 18:43, Corey Richardson wrote: On Wed, Apr 2, 2014 at 1:34 PM, Steve Klabnik st...@steveklabnik.com wrote: I compiled from source just yesterday, but everything's been going swimmingly! I just have one comment on 0.10: It seems like println was removed from the prelude. While I can totally appreciate that most people will use println!, which is automatically use-able, it _is_ making my 'hello world' examples significantly more complex, since basically every one of them needs to either import println or use println!({}, foo); I'm not sure if this is a good or bad thing, just wanted to raise that as a possible issue. It has been raised, as an extension to the macro, that invocation with a single, non-string literal, could expand into `println!({}, $that_arg)` rather than requiring the `{}`. This sounds even better than having both println() and println!() (in the prelude) with non-obvious differences. This was discussed a while ago. I am very strongly opposed to this change. The primary reason being that println!(hello world); and let s = hello world; println!(s); should have the same semantics. I don't believe we have any precedence right now for a semantic behavior change when using an identifier in place of an expression. Similarly, println!(hello world); and println!(hello + world); should behave the same. As with the previous, I don't believe we have any precedence for a semantic behavior change when replacing a constant string with a non-constant expression. -Kevin Ballard ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] 0.10 prerelease testing
Perhaps we should have `print` and `println` back in the prelude and call these `printf!` and `printfln!`. I think it would be a lot clearer, as people always ask how these are different from `print` and `println`. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] 0.10 prerelease testing
On Apr 2, 2014, at 10:14 PM, Daniel Micay danielmi...@gmail.com wrote: Perhaps we should have `print` and `println` back in the prelude and call these `printf!` and `printfln!`. I think it would be a lot clearer, as people always ask how these are different from `print` and `println`. I would not be opposed to putting print() and println() back in the prelude, but printf!() and printfln!() are not good names. Out format syntax does not match printf(), and any attempt to use the name printf would only sow confusion. Ultimately, though, I think things are fine as they are. In practice I haven't had any issue with the requirement to say println!({}, s) if I want to print a variable. And most of the time it turns out I want to print more than just a variable anyway. -Kevin ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev