Skip to content

Cannot use select! with Receiver contained in a struct #12902

Closed

Description

@quux00

The select! macro doesn't work if you try to reference a Receiver (Port) in a struct. huon reviewed this in the IRC room and agreed I should file an issue on it.

Here are two attempts to do this and both fail at compile time:

use std::io::Timer;

struct A {
    c: Sender<~str>,
    p: Receiver<~str>
}

fn does_not_compile() {
    let (ch, pt): (Sender<~str>, Receiver<~str>) = channel();
    let a = A{c: ch, p: pt};
    let mut timer = Timer::new().unwrap();
    let timeout = timer.oneshot(1000);
    select! (
        s = a.p.recv() => println!("{:s}", s), // error: no rules expected the token `.`
        () = timeout.recv() => println!("time out!")
    );
}



fn also_does_not_compile() {
    let (ch, pt): (Sender<~str>, Receiver<~str>) = channel();
    let a = A{c: ch, p: pt};
    let mut timer = Timer::new().unwrap();
    let timeout = timer.oneshot(1000);
    let p = &a.p;
    select! (
        s = p.recv() => println!("{:s}", s), // see error below
        () = timeout.recv() => println!("time out!")
    );
}

/*
<std macros>:7:37: 7:41 error: mismatched types: expected `&std::comm::Receiver<<generic #45>>` but found `&&std::comm::Receiver<~str>` (expected struct std::comm::Receiver but found &-ptr)
<std macros>:7 $( let mut $rx = sel.handle(&$rx); )+
^~~~
<std macros>:1:1: 15:2 note: in expansion of select!
selfport.rs:21:5: 24:7 note: expansion site
selfport.rs:22:42: 22:43 error: cannot determine a type for this bounded type parameter: unconstrained type
selfport.rs:22 s = p.recv() => println!("{:s}", s), // error: no rules expected the token `.`
*/

Activity

  1. huonw commented on Mar 15, 2014

    @huonw
    Contributor

    cc @alexcrichton

    Maybe the syntax could be changed to something like

    select! {
        s from a.p => ...,
        () from timeout => ...,
    }

    so that the port can be a full expression. (Although, those idents are used for new variable names... so this may be tricky.)

  2. alexcrichton commented on Mar 15, 2014

    @alexcrichton
    Member

    The macro is what tripped me up when I wrote, this. Ideally you want something like:

    $expr . $ident ()
    

    But $expr will swallow the ident, so I had to force the left-hand side to be an ident. But this is all why this select! macro is experimental.

  3. apoelstra commented on Jun 18, 2014

    @apoelstra
    Contributor

    I've hit this and it seems I can't move forward until it's resolved. (Well, "can't move forward" is strong; for now I'll just unwrap the macro manually.)

    Is there any consensus on how this should be resolved? I'm happy to cut code but it's seems pretty open what direction to go in.

  4. apoelstra commented on Jul 10, 2014

    @apoelstra
    Contributor

    Here is my suggestion:

    select!{
      obj1 from rx1 => {
        // Do stuff with obj1
      },
      obj2 from rx2 => {
        // Do stuff with obj2
      }
    }

    If people want to use something other than .recv() they can, using the alternate syntax

    select!{
      obj1_opt from rx1 using recv_opt => {
        // Do stuff with obj1_opt
      },
      obj2_opt from rx2 using recv_opt => {
        // Do stuff with obj2_opt
      }
    }

    Here is a sample implementation which makes no use of the Select structure or unsafe code or allocations. Thanks much to @eddyb on IRC for helping to clean this up.
    https://gist.github.com/apoelstra/1b12e849f537485e970b

    Note that this is "all or nothing" regarding the optional using syntax. Maybe someone with better macro-fu can come up with one where some options can have using and others not.

  5. mackwic commented on Jul 30, 2014

    @mackwic
    Contributor

    I just hit this bug on my code. Had to expand all our use of select! because of ownership errors.

    Maybe multiple macros for multiple uses will do the trick ?

  6. apoelstra commented on Aug 29, 2014

    @apoelstra
    Contributor

    Hi guys, I have been using the select! implementation in my own code for a month now and it seems to be working ok. What needs to be done to get it merged?

  7. thehydroimpulse commented on Sep 26, 2014

    @thehydroimpulse
    Contributor

    @apoelstra Did you ever send a PR for it?

  8. apoelstra commented on Sep 26, 2014

    @apoelstra
    Contributor

    No, I'll do that now.

  9. added a commit that references this issue on Sep 27, 2014
    c15ac5c
  10. tripped commented on Aug 19, 2015

    @tripped

    I just ran into this issue as well. It's especially vexing since the struct that contains the receivers must be borrowed as a mutable reference inside the select! -- so this fails:

    fn do_something(&mut self) {
        let r1 = &self.r1;
        let r2 = &self.r2;
        select! {
            a = r1.recv() => a.unwrap()(self), // ERROR! self.r1 is already borrowed!
            b = r2.recv() => (),
        }
    }

    It seems that the only reason select! only allows expressions of the form "ident.ident()" is so that the macro can use that first ident as a unique local variable name inside the expansion.

    This could be solved if we could generate unique symbols in a macro expansion. Issue #9031 mentions this but was closed back when macro hygiene was implemented. Sadly, macro hygiene does not help here, since all the identifiers we need to distinguish have the same syntax context.

    I've been poking and prodding at the macro syntax trying to figure out a way to do this that isn't horrible, special-cased, or just plain inconvenient, but I'm not sure it's reasonable to do without the ability to generate symbols. Maybe we do need a syntax extension after all?

  11. tripped commented on Aug 19, 2015

    @tripped

    Looking at it with a little less sleep deprivation, I think the current syntax of the select! macro just doesn't make sense. It uses a matcher for the method invoked on the receiver -- ostensibly supporting receivers whose receive method is spelled differently than recv()? However, it does not use that method name to invoke a member of the receiver named by $rx:ident, it invokes it on a Handle, which only supports recv(). (And Handle's implementation requires that the thing selected on be a Receiver<T> and calls its .recv()).

    Removing the spurious .recv() from the required syntax does free up the macro to use an expr for the receiver. Then the only remaining difficulty is generating a unique variable name for each handle in the expansion. Perhaps a slight modification to the macro system would help here? I may just try to hack something together and see if it works.

  12. tripped commented on Aug 19, 2015

    @tripped

    Apparently I was wrong about not being able to do this with the current macro system. User talchas on #rust came up with this formulation that allows the use of an arbitrary expression as the receiver in a select! expansion: http://is.gd/F6Wy5D

    A little bit hairier than the original, and it doesn't solve my borrowing problems, but I think it does make the macro much more generally useful. Any thoughts?

  13. jFransham commented on Dec 1, 2015

    @jFransham
    Contributor

    @tripped just a simple change to your suggestion, to stop macro clutter (this style is used in a bunch of internal macros) http://is.gd/bMLJK9

  14. alexcrichton commented on Aug 1, 2017

    @alexcrichton
    Member

    The select! macro is sort of defacto deprecated nowadays in the sense that "selection over a number of events" is best done with the futures crate outside the standard library. In that sense we're not going to be changing the current select! and as a result, I'm going to close this.

  15. added a commit that references this issue on Aug 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-concurrencyArea: ConcurrencyC-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-libs-api[DEPRECATED; DO NOT USE]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions