[rust-dev] How to manage multiple interacting I/O streams?
Hi everybody, after using Rust for the last few months to get used to it (I really love it), I recently started to play around with the new I/O, especially the networking stuff. I'd say that I'm familiar with how std::rt.:io, std::task and std::comm works. However I'm not sure how I should handle multiple I/O streams. Let's say I'm building a simple chat server where people can connect on a TCP port, set a name and broadcast messages to everyone else. For each accepted incoming stream, I'd spawn a new task and pass the stream to it. Or actually I'd create a struct Connection that contains the stream and connection related information, like the user's name. Every child task would run a loop that reads a line from its stream and processes it. It should either broadcast to all other connections or do something else like setting the name. How can one connection write stuff to the others if it doesn't know about them? If I use a chan/port for every connection to report text to be broadcasted back to the main task, the broadcast needs to take place in the main task. But the main task can not hold a list of connections since every connection has moved to its own task where the reading loop takes place. There could be a second chan/port for every connection that allows the main task to send something to a connection, but how would a connection's task be supposed to recv from the port when it's generally waiting to read from the stream most time? I'm also unsure how the main task should be able to iterate all connection, e.g. if somebody wants to list all connected users. I'd appreciate any hint on how this can be done the Rust-way. (I thereby want to prevent using @-boxes at all) Thanks, Andreas ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Should I/O use conditions?
There have been some recent grumblings [1] about the use of conditions in the new runtime's I/O system. It was decided awhile back [2] that we'd try out the use of conditions for error handling in the new runtime. This has manifested itself in the `io_error` and `read_error` conditions inside of the `rt::io` module. I've done some recent work [3][4] in removing std::io, and I've personally found the use of conditions a little painful. Here's a few of the problems that I ran into: * Do you catch io_error or read_error? Catching both is really painful, and remembering which one to catch is sometimes more painful. This could be alleviated by having a read_error raise on an io_error if there is no handler registered, so this is certainly not a show stopper. * If you want to catch an error, it's pretty ergonomically painful let mut err = None; let ret = do io::io_error::cond.trap(|e| err = Some(e)).inside { // some i/o operation }; match err { None = {} // yay we succeeded Some(e) {} // oh no, an error! } This seems like a lot of verbage for doing something as simple as checking to see whether an i/o operation resulted in an error or not. * Using closures prevents certain kinds of movements between values. This is a known limitation of closures today and may be alleviated by the closure redesign, however. This isn't quite a show stopper, but it is another pain point. * The blanket error condition isn't really being used for what I thought conditions were intended for, which is somehow continuing the work at the raise site using the result of the condition. Because the io error conditions both return (), then there's not really anything you can do but hope to squirrel away the conditions somewhere. These various reasons have added to the pain points when dealing with error handling in the new runtime. I certainly don't think that we should remove conditions entirely (they're an effective error handling system for many purposes), but for the use case of simply reporting errors I don't feel that they're quite appropriate. All that being said, we're not making much progress without an idea of where to possibly go next. Right now, the current idea is to create an Error trait and have I/O operations return ResultT, ~Error instead of OptionT. This would mean that Reader/Writer and other associated traits would be defined for ResultT, ~Error where T: Trait (so you could still chain operations easily), and you could very easily check for an I/O error. This is all still pretty early-stage, though, and we certainly haven't committed to anything just yet. I wanted to canvas everyone to see what others' opinions are about using conditions for error handling. I'm certainly not the only use-case so far! [1] https://github.com/mozilla/rust/issues/9795 [2] https://mail.mozilla.org/pipermail/rust-dev/2013-April/003746.html [3] https://github.com/mozilla/rust/pull/9749 [4] https://github.com/mozilla/rust/pull/9888 ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
On 10/16/13 11:02 AM, Alex Crichton wrote: All that being said, we're not making much progress without an idea of where to possibly go next. Right now, the current idea is to create an Error trait and have I/O operations return ResultT, ~Error instead of OptionT. This would mean that Reader/Writer and other associated traits would be defined for ResultT, ~Error where T: Trait (so you could still chain operations easily), and you could very easily check for an I/O error. +1 for the use of Result. Yay monads! Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
+1 here too. I agree with what Alex said about conditions. They're useful for when you can actually recover from the error gracefully, but for generic error reporting they're kind of a PITA. -Kevin On Oct 16, 2013, at 11:16 AM, Patrick Walton pwal...@mozilla.com wrote: On 10/16/13 11:02 AM, Alex Crichton wrote: All that being said, we're not making much progress without an idea of where to possibly go next. Right now, the current idea is to create an Error trait and have I/O operations return ResultT, ~Error instead of OptionT. This would mean that Reader/Writer and other associated traits would be defined for ResultT, ~Error where T: Trait (so you could still chain operations easily), and you could very easily check for an I/O error. +1 for the use of Result. Yay monads! Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev smime.p7s Description: S/MIME cryptographic signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
+1 on getting rid of IO conditions. I'd be okay with a Result based API. If we change it over, we should have a tutorial that goes over how to deal with Results to make it less painful for people (e.g. using or_return! macros). I'm not a huge fan of implementing Reader for ResultReader, ~Error, etc. One thing that I've tried that I kind of like is having separate `foo` and `try_foo` methods, where `try_foo` returns a Result and `foo` is a wrapper around `try_foo` that fails with a good error message if `foo` returned Err. It makes it more explicit to users of the API what's going to happen, and when failure will occur if there's an error. A failed ResultT, ~Error can hang around for a long time before any methods are called on it. It can also allow for slightly better failure messages. For example, if `prepare` fails, its error message can print the query string without having to copy it into a new ~str. In the IO case, you could keep this cost free to implement by having `Reader` contain a default implementation for `read` and only require users to implement `try_read`. See http://docs.octayn.net/postgres/struct.PostgresConnection.html for some examples. Steven Fackler On Wed, Oct 16, 2013 at 11:20 AM, Kevin Ballard ke...@sb.org wrote: +1 here too. I agree with what Alex said about conditions. They're useful for when you can actually recover from the error gracefully, but for generic error reporting they're kind of a PITA. -Kevin On Oct 16, 2013, at 11:16 AM, Patrick Walton pwal...@mozilla.com wrote: On 10/16/13 11:02 AM, Alex Crichton wrote: All that being said, we're not making much progress without an idea of where to possibly go next. Right now, the current idea is to create an Error trait and have I/O operations return ResultT, ~Error instead of OptionT. This would mean that Reader/Writer and other associated traits would be defined for ResultT, ~Error where T: Trait (so you could still chain operations easily), and you could very easily check for an I/O error. +1 for the use of Result. Yay monads! Patrick ___ 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 mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
What about the idea of making Result cause task failure if it is destroyed in the error case? (as opposed to destructuring it) This just needs a simple language change to add an attribute that would be applied on Result to declare that it's OK to destructure it and cause drop() to not be called. In addition, maybe some EH syntax sugar like the one I proposed in http://www.mail-archive.com/rust-dev@mozilla.org/msg04093.html ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
On 10/16/13 1:44 PM, Bill Myers wrote: What about the idea of making Result cause task failure if it is destroyed in the error case? (as opposed to destructuring it) For a long time now I've been wanting to make unused non-unit return values a warning. I think this would be a perfect time to do that... Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
This seems rather heavy-weight to me. I’d much prefer if such a thing was opt-in, e.g. by a function attribute that says the return value must be handled. -Kevin On Oct 16, 2013, at 2:12 PM, Patrick Walton pwal...@mozilla.com wrote: On 10/16/13 1:44 PM, Bill Myers wrote: What about the idea of making Result cause task failure if it is destroyed in the error case? (as opposed to destructuring it) For a long time now I've been wanting to make unused non-unit return values a warning. I think this would be a perfect time to do that... Patrick ___ 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] Should I/O use conditions?
On Oct 17, 2013 8:12 AM, Patrick Walton pwal...@mozilla.com wrote: For a long time now I've been wanting to make unused non-unit return values a warning. I think this would be a perfect time to do that... I would love this to be the case. It's the biggest problem I had with Go's error handling: that it's far too easy to just ignore a return value accidentally. (Truth to tell, the problem is far worse in Go because for some reason they decided you could take not all of the return values, e.g. for a func returning (T, err), `t = f()` will silently discard the err; this has been a decision that has baffled me; I'm glad we have generics and Result and tuples-as-real-types to fix that bad idiom!) Truly explicit is better than implicit. I have three remaining questions on this feature: 1. Is there a functional difference between the lines `let _ = f()` and `let _foo = f()` (assuming no usage of the variable)? These will probably become common if this check is in place. 2. When will Drop happen? I suspect this would make it happen at the end of the callee block, whereas silent ignoring probably before returning execution to the callee? 3. Would such a thing be able to be overridden with something along the lines of #[allow(unused_return_values)]? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Should I/O use conditions?
On 10/16/13 3:31 PM, Chris Morgan wrote: 1. Is there a functional difference between the lines `let _ = f()` and `let _foo = f()` (assuming no usage of the variable)? These will probably become common if this check is in place. There's no difference except that let _ runs the destructor immediately whereas let _foo runs the destructor at the end of the block. (This is admittedly subtle.) 2. When will Drop happen? I suspect this would make it happen at the end of the callee block, whereas silent ignoring probably before returning execution to the callee? See above. 3. Would such a thing be able to be overridden with something along the lines of #[allow(unused_return_values)]? Yes, and on a per-callee basis as well. (Lots of C functions want return values to be ignorable, so this should be able to be specified.) Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Proposal for macro invocation sugar
Hello! I was thinking a bit about macros, and I had an idea for a kind of syntactic sugar that might be useful to have. I also posted this to the issue tracker at https://github.com/mozilla/rust/issues/9894. # Current situation Right now, rust knows about two kinds of macro invocation: IDENT!(...) IDENT! IDENT (...) The latter one is just used by `macro_rules!` right now, and seems kinda out of place because of that. Additionally, just being restricted to `IDENT!(...)` means that, while you can define macros just fine, the resulting invocation syntax often looks a bit weird because of the need for the outer `()` pair. For example, if you want to write some kind of custom `match` macro you ideally want a syntax like `macro! EXPR { CASES... }`, but in practice are forced to decide between redundant, deeply nested brackets or weird syntax if you want to reduce the brackets: ~~~ my_match!( foo().bar().baz() { case 1 = ... case 2 = ... ... } ) my_match!(foo().bar().baz() cases: case 1 = ... case 2 = ... ... ) ~~~ # Proposal We can't just allow macros to accept different syntax like `IDENT! EXPR ( ... )`, because it would create ambiguity in the parser, but it occurred to me that we _can_ provide syntactic sugar for transforming 'nicer looking' variants into the regular `IDENT!(...)` syntax. Basically, I'm thinking of leveraging the bang in a macro invocation to annotate how many following bracket pairs to group into one regular macro invocation: ~~~ IDENT!! (...) (...) = desugaring = IDENT!((...) (...)) IDENT!!! (...) (...) (...) = desugaring = IDENT!((...) (...) (...)) ... etc ~~~ The number of bangs could become confusing fast, but I don't expect that macros with more than two bracket groups are going to be common. And because it would just be sugar, you could always write it as the regular form. # Advantages There are a number of advantages I see with this proposal: 1. The two macro invocation forms can be folded into one: ~~~ IDENT!(...)= IDENT!(...) IDENT! IDENT (...) = IDENT!! (IDENT) (...) == IDENT!((IDENT) (...)) ~~~ 2. Custom syntax can become nicer looking, especially for control structures. Looking at the `my_match` example: ~~~ my_match!! (foo().bar().baz()) { case 1 = ... case 2 = ... ... } ~~~ ... which looks more natural than any the two options outlined above. 3. It's pure syntactic sugar, which means it's easy to implement and reason about. All `libsyntax` needs to do is to consume a list of bracket-counted token trees equal to the number of bangs, and introduce an artificial outer bracket pair if the number is higher than one. 4. It's just invocation sugar, which means there is no difference between defining a macro that uses this vs one that doesn't - you just declare them all assuming the explicit outer `()` pair. # Potential issues The possible issues I can see with this are confusing error messages if the number of bangs is wrong, and uncertainty about which brackets belong to a macro invocation and which are regular rust code if their contents become big and/or their number become high. However, in theory rust might be able to provide good error messages for the first one, because if the macro name is right you'd get an `No rules expected this ...` error, and there could be some heuristics for recognizing cases where the user has do add/remove bangs to match the macro definition. And the second one can likely be managed by syntax highlighting, and by the usual convention of not abusing the syntax to the point where it becomes illegible. # Backwards compatibility Adding this sugar would be backwards compatible because it doesn't change any existing macro syntax, however if `IDENT! IDENT (...)` ends up obsolete through this, that would break existing uses of `macro_rules!` - but macros are behind a feature flag anyway at this point, so it wouldn't matter as much. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Audio for Rust: A Friendly Introduction now available
Hi Tim, Thanks for posting these. I haven't listened to the audio, but I read through the slides and I like your presentation. For me, it is a good reminder of why Rust is so cool. I don't know about other users (survey time, anyone? [?]), but my background is mostly C++ and Java. The notes about C++ vs. Rust are helpful. Cadence On Tue, Oct 15, 2013 at 2:28 PM, Tim Chevalier catamorph...@gmail.comwrote: Hi all, The audio recording for my June 2013 talk at Open Source Bridge, Rust: A Friendly Introduction, is now available: http://opensourcebridge.org/sessions/970 As far as I know, there was no video recording, but the slides are linked from: http://opensourcebridge.org/wiki/2013/Rust%3A_A_Friendly_Introduction Cheers, Tim -- Tim Chevalier * http://catamorphism.org/ * Often in error, never in doubt Being queer is not about a right to privacy; it is about the freedom to be public, to just be who we are. -- anonymous, June 1990 ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev 330.gif___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev