[Idea] Homeless Structs

Homeless Structs idea is a bit similar to "Anonymous" Structs and "Unnamed" Structs. With only one difference - they are neither anonymous not unnamed, because these Structs have names!

fn getMyStruct(x: i64) -> MyStruct :{f1: i64, f2: i64} {
    MyStruct :{
        f1: x,
        f2: x * 2,
    }
}
  1. Homeless Struct is never define via struct
  2. Name of homeless struct has special mark (for example ":{" at the beginning)
  3. Type include name and description of all fields
  4. Name collision is forbidden
  5. Since struct is homeless it avoids orphan rule for implementations: it is Ok to write implementations everywhere!
  6. Maybe a new syntax is needed for deriving
#[derive(<MyStruct: {f1: i64, f2: i64}>::Debug)]

I think you might be misjudging the purpose of the orphan rules. Ordinary structs (or enums) having a specific crate that defines them – a “home” if you will to use your terminology here – is not a burden but a power. Orphan rules don’t exist to protect the “implementation rights” of some “definer” and “owner” of a struct, no:[1] Orphan rules exist to ensure proper coherence, two independent crates shall not be able to write (conflicting versions of) the same implementation of a trait for a type, without knowing of each other (i.e. one depending on the other)!

For the simple reason that writing an implementation for a trait means you need to depend on the defining crates of the trait and type(s) in question, those crates can gain the power to write trait implementation without any risk of coherence issues. For your “homeless” structs, no crate could hold the powers of the defining crate of the struct – consequently, it would be not okay to write trait implementations anywhere (except perhaps the crate that defines the trait in question).


  1. well… technically, I guess there are also aspects to orphan rules that only serve to protect some “rights” for the defining crate to add trait implementations without semver-breakage, but these technicalities are not really relevant to my point ↩︎

11 Likes

Ok, but tuples are also homeless structs with a well known name ((i32, u32) could be called, under this scheme, Tuple2 :{0: i32, 1: u32)). You can still impl traits for tuples.

Could the same rule that enables writing impl MyTrait for (i32, u32) be used to impl traits for homeless structs?

But what's needed to make this practical is a way to impl a trait for all homeless structs, whenever appropriate; that is, a way to be generic on the homeless struct name (which means that the stdlib could add some common traits). I mean it would be very awkward if those structs couldn't impl Default and such.

No, tuples are treated as foreign types always (essentially their defining crate is “the compiler”). They obey the orphan rules to determine when you are allowed to implement traits for them.

15 Likes

In that case will be no extra benefits for having such structures at language.

To have an ability for homeless structs to write implementations everywhere will be much better option!

This is not "avoid orphan rule". This is "get rid of orphan rule". While orphan rule has pros and cons and whether a new programming language should have orphan rule is an interesting question, I don't think it is an appropriate question for Rust at this time.

2 Likes

Seems you want to be able to define the struct in the position where it is referenced. I didn’t get the point why it is related to orphan rules.

What if get_my_struct() and get_my_struct_too() both return MyStruct? Do we need to repeat the fields, or just use -> MyStruct in the second function?

By the way, the crate structx provides something similar:

use structx::*;

fn get_my_struct(x: i64) -> Structx!{f1: i64, f2: i64} {
    structx!{
        f1: x,
        f2: x * 2,
    }
}

And the name of the struct is structx_f1_f2.

2 Likes

I think a more interesting way for side stepping the orphan rule would be to explicitly allow it with an annotation like #[allow(orphan_implementation)]. I can see two uses for it:

  1. Allowing crates do implement a trait defined in another crate, without forcing a dependency (e.g. tokio and futures).
  2. Allowing application code to side step the rule. Since there are no dependants for the crate, no downstream crates can be broken from liberally bypassing the rule.