Shorthand init

When initializing a data structure (struct & class) with named fields, allow writing propertyName as a shorthand for self.propertyName = propertyName . This allows a compact syntax for initialization, with less duplication.

Example:

struct Point {
	let x: Int
	let y: Int

	init(x: Int, y: Int) {
		self.x = x
		self.y = y
	}
}

For now we can't write .init() like this:

struct Point {
	let x: Int
	let y: Int

	init(x: Int, y: Int) {
		x; y
	}

	init(x: Int, y: Int) {
		x
		y
	}
}

Because the parameter names and the struct field names are exactly the same.

What does the community think?

1 Like

Are you writing an 'init' there because you need to make it 'public'? I'd rather leave it out entirely, and have Swift generate it.

It would also be handy if Swift let me specify its generated init not use labels, but consider the order I declare them in. Ie:

struct Point {
   let _ x: Int
   let _ y: Int
}

let pt = Point( 123, 456 )

2 Likes

If the init is that simple, it can be omitted. If you need a different access level, you should try and revive the pitch from a while back to allow synthesized init with access levels other than internal.

If that syntax would be interspersed with other logic, I'm a huge no way. That syntax is already legal, though you'll get a warning for an unused value. Giving it a brand new meaning inside of init would be terribly confusing. There's also no indication that assignment is being done.

7 Likes

If there was a shorthand syntax I would prefer it to be even shorter:

struct Point {
	let x: Int
	let y: Int

	init(self.x: Int, self.y: Int) {}
}
3 Likes

Such a shorthand already exists:

struct Point {
	let x: Int
	let y: Int
}
2 Likes

Yes, but you cannot mix it with non-autoassigned parameters. Also doesn‘t work with classes and you cannot run code after the assignments in the init method (I think).

Your shorthand has no code. Are you suggesting that parameters would be automatically assigned to properties of the same name?

Yes, sorry, didn‘t make that clear. Extended example:

class Point {
    let x: Int
    let y: Int
    let z: Int

    init(self.x: Int, self.y: Int) {
        z = x * y
    }
}

The self. prefix would mean „assign to property with same name“.

7 Likes

I think the justification from writability (how annoying it is to type all this stuff out manually) is very weak, because it's likely that you only have to do this once, when first declaring the struct. For a developer, some days are just more boring than others. :slight_smile:

A justification from readability might carry more weight (because the code is likely to be read a lot more often it's written), but in this case I think readability suffers, because the proposed syntax is so contextual. Bulky though it is, you can't say that this sort of thing isn't crystal clear:

	init(x: Int, y: Int) {
		self.x = x
		self.y = y
	}

However, I think a better solution is to make this a behavior of Xcode and IDEs, where they would retrieve the synthesized text (of an omitted initialization) from the compiler, and use that to provide fixits for properties that were not initialized before the end of the init. This would be roughly similar to the IDE adding stubs for missing protocol conformances.

That approach would also tie into a larger discussion about "instantiating" synthesized code in general. There are a few places we'd like this, including Codable conformance, to take a very obvious example.

5 Likes

One thing to consider is that it's not only a tax on writing, but also on renaming properties. So a result of this redundancy is that properties may be named less optimal (on average) than they would be if renaming them was less work.

It's crystal clear unless you accidentally wrote

init(x: Int, y: Int) {
    self.x = y
    self.y = x
}

@lassejansen's proposal makes that kind of error impossible.

5 Likes

I'd love to have something like:

struct Point {
    let x: Int
    let y: Int
}

struct Segment {
    let start: Point
    let end: Point
}

Segment(
    start: (x: 0, y: 0),
    end: (x: 1, y: 1)
)

I've many times wished for an ExpressibleByTupleLiteral sort of protocol.

2 Likes

This would be amazing.

One idea that has been nesting in the back of my head for a bit is that we could allow type placeholders in expressions, allowing this:

Segment(
    start: _(x: 0, y: 0),
    end: _(x: 1, y: 1)
)

In my opinion, having

  1. just a single line propertyName
  2. which is sugar for self.propertyName = propertyName
  3. and only works within an init statement (presumably)

Just feels too unseen-magic-y. I also feel like this syntax starts to become a lot less clear when you have to do some processing within the initializer. For example,

struct Person {
    let name: String
    let occupation: String?

    init(firstName: String, lastName: String, occupation: String?) {
        self.name = firstName + " " + lastName
        occupation
    }
}

The case above is already quite simple, but I feel like it demonstrates how unclear this could end up being in a larger example since there is no indication of assignment at all.

The problem that this seems to address is making member wise inits easier to create - like Charles_Constant has said, the only reason I see this being necessary is if you need to make the member wise initializer public, or if you have multiple initializers and want one of them to be the member wise one.

I think the better solution for that would be Explicit Memberwise Initializers - #3 by dan-zheng. A member wise init under that suggestion would look like init(internal...).

Alternatively, a @MemberwiseInit macro, which just autogenerates a member wise init, might work as well.

2 Likes

Couldn't agree more.

In general though, I consider writing out initializers like this a minor inconvenience. And not enough of a problem to add more magic to the language.

1 Like

While I appreciate the motivation behind simplifying initializers, I personally don’t believe this new syntax improves clarity or reduces meaningful complexity.

The current self.x = x form is explicit, readable, and beginner-friendly. It makes the relationship between parameters and stored properties immediately clear, especially to developers who are just starting with Swift or coming from other languages. Introducing a more implicit shorthand like:

init(x: Int, y: Int) {
    x
    y
}

may save a few keystrokes, but at the cost of readability and learnability. It introduces an abstraction that isn’t immediately obvious.

6 Likes