Struct sugar

I think we should drop default values for struct members, since they are confusing and less flexible than non-static expressions.

With struct sugar, it will take little effort to write a constructor with the default values per member.

An idea to improve refactoring is to use a colon after ‘pub’ to make all members public.

pub: struct Foo {
    bar: uint,
    baz: uint
}

We could match against argument name when one argument uses named syntax:

// These calls do the same
foo(x, y, z: 0);
foo(y, x, z: 0);
foo(z:0, y, x);

If a name does not match, there will be a compiler error.

One idea from discussion on reddit:

To avoid special casing on Option, we could have a rule that the first enum variant with no type signature is matched against. There must be 2 variants, one empty and one with a single type. The reason it can only be a single type is because it needs to be unwrappable and the internal type must be the unwrapped type.

For example:

pub enum MoviesLimit {
    AllMovies,
    Number(uint)
}

fn get_best_movies(&self, limit: MoviesLimit = self.len()) { ... }

This will require typing the full type.

fn foo(&self, limit: Option<uint> = 2);

One idea is to use the returned value of the Default trait to match against.

We can add a new trait ‘Optional’ that generalizes types that can be used for optional arguments.

pub trait Optional<T> {
    fn none() -> Self;
    fn some(val: T) -> Self;
    fn to_option(self) -> Option<T>;
}

impl<T> Optional<T> for Option<T> {
    fn none() -> Option<T> { None }
    fn some(val: T) -> Option<T> { Some(val) }
    fn to_option(self) -> Option<T> { self }
}

‘Optional’ is used in the desugar and solves generic issues. The desugar is attempted whenever the types do not match. This can be used in generics.

fn foo<T: Optional<U>, U: Default>(bar: T = Default::default()) { ... }

foo(x);
foo(..);

This desugars into

fn foo<T: Optional<U>, U: Default>(bar: T) {
    let bar = match bar.to_option() {
        None => { Default::default() }
        Some(val) => val
    };
    ...
}

foo(Optional::some(x));
foo(Optional::none());

With a concrete type and early return:

fn foo(bar: Option<uint> = return) { ... }

foo(2);
foo(..);

This desugars into

fn foo(bar: Option<uint>) {
    let bar = match bar.to_option() {
        None => { return }
        Some(val) => val
    };
    ...
}

foo(Optional::some(2));
foo(Optional::none());

With struct argument:

fn foo(Foo { bar = return }) { ... }

foo(bar);
foo(..);

This desugars into

fn foo(Foo { bar }: Foo) {
    let bar = match bar.to_option() {
        None => { return }
        Some(val) => val
    };
    ...
}

foo(Foo { bar: Optional::some(bar) });
foo(Foo { bar: Optional::none() });

I think we should break it down into 2 RFCs, 1) for struct sugar with pub: and default arguments, 2) for overloading. Overloading should wait.

The current summary solves the concerns about struct sugar so far:

  • No special typing on Option
  • No implicit casting of tuples
  • No duality with struct tuples
  • No error when reordering members in a struct
  • Performance choice is made on the function side and implementation of Optional, therefore no downside by reducing line noise
  • Orthogonal to existing monadic patterns

I think at least one named argument should be required to use struct sugar, because if you refactor a function where swapping is intended, it can lead to error.

fn foo(x: uint, y: uint) { ... }

foo(y, x);

When refactored to a struct this will give a compiler error.

pub: struct Foo { x: uint, y: uint }

foo(Foo { x, y }) { ... }

foo(y, x); // error: expected Foo
foo(x: y, y: x); // fix

There is a problem with statements like Vec::new().as_slice(). When desugared into the match block, it does not outlive the match arm.

This can be solved by desugaring to this:

let _x;
let children = match children.to_option() {
   None => { _x = Vec::new(); _x.as_slice() },
   Some(val) => val
};

Or, one can use ["", ..0].as_slice()

Here is a draft of the first RFC, focusing only on struct sugar:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.