Skip to content

RFC: should closure type expr (|A| -> B) support an optional & sigil? #10720

Closed
@pnkfelix

Description

@pnkfelix

With new style closure type syntax (|A| -> B, or more generally <'lt>|A|:K -> B), one no longer includes a sigil. I believe the motivation for this was that the type |A| -> B can only be used with borrowed-pointers, and thus one can unambiguously remove the sigil.

The problem, which did not occur to me until last night (see concrete example below [1]), is that the sigiled borrowed pointer syntax &T is more generally &'lt T.

From what I can tell from looking at the parser code and from my own experiments, pcwalton's commit f27272d that added support for new-style closure types made sure that one can still express explicit lifetimes on new-style closure types, even without the sigil.

With commit f27272d, one writes the type like this: 'lt |A| -> B.

  • (Or more generally, 'lt1 <'lt2>|A|:K -> B)

However, I find it disconcerting to see that 'lt floating out there like that. Maybe I just need time to adjust, but I would prefer to keep the & for the cases when one needs to express the lifetime for the reference.


A coherent and non-ugly way to achieve the above goal, IMO, would be to adopt the following syntax:

  • |A| -> B is sugar for &|A| -> B. (This obviously is not a recursive desugaring.)
  • 'lt |A| -> B becomes illegal
  • If you need a lifetime, you have to write it out like so: &'lt |A| -> B

I see the above sugar as having precedent in C function pointers, where

int doub(int x) { return x + x; }
struct Dotwice {
    int (*f)(int);
    ...
};
int main() {
    Dotwice quad;
    quad.f = &doub;
    // is same as quad.f = doub;
    // but not same as quad.f = &&doub;
}

I have not thought through whether an analogous sugaring should be applied to Rust's lambda expression syntax, or even could be. The only case where it would arise would be when you would need to feed in an explicit lifetime for the reference you create when you construct a closure, i.e.:

    let dotwice : <'a>|f:&'a |int| -> int| -> (&'a |int| -> int)
        = |f| { &'a |x| f(f(x)) };

which cannot currently be expressed at all AFAICT. (I think it can make sense in Rust's memory model, though I might not have all the necessary bounds encoded above.)


(I might also have missed some of the relevant conversation here. I.e. perhaps there is some future goal to extend the kind-bounds K on the closure type to allow one to put the lifetime constraint for the reference there; right now the only lifetime allowed there is 'static.)


[1] Concrete example of where the above need to write 'lt |A| -> B arises:

fn main() {
    let double = |x:int| { x + x };
    trait Xform<T> { fn x(&self, T) -> T; }
    struct Dotwice<'a, T> { f: 'a|T| -> T }
    impl<'a, T> Xform<T> for Dotwice<'a, T> {
        fn x(&self, t:T) -> T { (self.f)((self.f)(t)) }
    }

    let quadrupler = Dotwice{ f: double };
    let eight = quadrupler.x(2);

    println!("eight: {}", eight);
}

cc @pcwalton @nikomatsakis

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions