Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: A way to guarantee to the compiler that a certain field exists #3165

Open
mahmoud-moursy opened this issue Aug 21, 2021 · 2 comments

Comments

@mahmoud-moursy
Copy link

Currently in Rust, there is no way to guarantee to the compiler that something for sure has a certain field. For example, if you are using an enum that has multiple fields, and all of those fields have the exact same name, type and purpose. I want a "semi-struct" of sorts (or maybe just a struct) that can guarantee that all variants inside of an enum have a certain field. Maybe this could also see extended use in traits or functions, to guarantee that XYZ field exists.

I have no idea how the syntax should (ideally) be structured, but here is a proposal that I have in Rust pseudocode.

The drawbacks that I see for this are in the pseudocode's comments.

// 'strict' will be important later
strict field FooField {
  x: usize,
  y: usize
}

field BarField {
  x: usize,
  y: usize
}

// The 'strict' specifier now comes into play here. Now you can automatically implement a new
// function for types that don't implement it and also have the FooField field.
trait FooTrait {
  // I think the dyn keyword should be required
  // for non-strict fields like BarField.
  
  // You also shouldn't be able to initiate a type
  // from Output unless it used a "strict" field.
  type Output: FooField;
  // anonymous fields with the exact names and properties may be
  // used instead of FooField, however ALREADY DEFINED fields
  // with the exact same properties should be disallowed

  fn new(x: usize, y: usize) -> Output {
    // Here, in theory, you should be able to
    // create a new instance since this is a strict field
    Output {
      x,
      y
    }
  }
}

// Implementing MULTIPLE fields on a tuple would also be problematic
// because it uses numbers to access values, as opposed to their names.
field BarTuple(x: usize, y: usize);

// Using anonymous parameters for the declaration above is probably 
// just a bad idea because it makes the purpose of these values 
// less clear.

// =============

fn main() {
  // Anonymous fields.
  let my_thing = field {
      x: 3u8,
      y: 4u8
  };
  other_fn(my_thing);
}

fn other_fn(thing: field {x: u8, y: u8} ) {

}

// Above, the syntax is a bit clunky.
// I think the real advantage would be being able to
// create objects without the need for an enum or
// an entirely new struct, not for usage in functions.

// Should there be a field! macro to allow for this?

// =============

// Here, defining FooStruct is optional since we are not adding additional properties.
struct FooStruct with FooField;

// Here, it is not. The goal here is to make it more difficult to mis-interpret the code while skimming through it.
struct BarStruct with FooField {
    x, y,
    z: usize
}

// Fields that are not strict can optionally be converted into strict
// fields for certain use-cases.
struct BarStruct with strict BarField;

enum FooEnum with FooField {
    // FooVariant does not compile!
    FooVariant {
      // Shorthand, so you don't need to specify types. 
      // Also makes it clearer which types come from the FooField, and which from the enum variant.
      x,
      y,
      // This throws a compiler error because 'strict' was specified. That means
      // that the enum cannot contain any additional fields.
      s: usize
   }
   // Fails, no X or Y fields
   BarVariant,
   NoIdeaVariant {
      // Success!
      x,
      y
   }
}

// All variants must have at least BarField's components,
// but can also add their own fields. Variants of BarEnum
// cannot be constructed in a trait function though, as they might require
// additional fields which won't be included
enum BarEnum with BarField {
   // Valid
   Pos3D {
     x,
     y,
     z: usize
   }
  // Valid
  Pos2D {
    x, y
  }
  // Invalid, does not contain field y.
  Pos1D {
    x
  }
  // Invalid, no implicit fields here!
  Pos0D
}

// Uses all of BarField's fields, but this time,
// it strictly requires that it conforms to BarField
enum xyzEnum with strict BarField {
  // Variants here.
}

The reason I do not wish to add new functionality to structs is because I want fields to be used like traits, while structs are strict types.

I would greatly appreciate criticism.

@kennytm
Copy link
Member

kennytm commented Aug 22, 2021

see also the postponed #1546.

@nielsle
Copy link

nielsle commented Aug 22, 2021

And #142 for a bit of history

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants