[Solved] Compile error when putting init() in a separate file

See the code below. Putting it in the same file works fine, but putting it in two files causes compile error. I wonder why?

Based on the information here, the x=1 line in init() should initialize the property wrapper. It seems that it fails to work for some reason in this case?

// File 1
@propertyWrapper
struct DummyWrapper<T>: Equatable where T: Equatable {
    var wrappedValue: T

    public init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
    }
}

struct Foo {
    @DummyWrapper var x: Int
}

// File 2
extension Foo {
    init() {
        x = 1 // Error: 'self' used before all stored properties are initialized
    }
}

Background: I'd like to put init() in a separate file because I'm thinking to put all structs conforminng to Codable in the same folder. And put their methods (including init()) in other folders.

1 Like

Normally, I would say that init is special in that it should live in the type declaration itself and not an extension. However, that particular error is unexpected.

1 Like

I see your point, though I often put init() in extensions for two reasons: a) just for organizing code, b) let the compiler synthesize the member-wise init(). I usually do them in the same file though.

BTW, the above error only occurs when the struct has property wrapper.

1 Like

This occurs because Foo could potentially have private properties that you would not be able to access from your extension. Therefore, there are situations where it would be impossible for an initialiser in a separate file to fully initialise all properties. That's why Swift requires you to call an init that is defined in File 1 before you access/modify any properties from within the init in File 2.

In this case you can make use of the default memberwise initialiser by replacing x = 1 with self.init(x: 1) in the init you define in File 2. This will fix the error. You could then have a line after that such as x = 2 and that would be perfectly valid because you've already initialised Foo.

4 Likes

Thanks. You're right. I made a change just now (see below). It also failed to compile but generated a different error, which indicated _x has private access.

-        x = 1
+       _x = DummyWrapper(wrappedValue: 1)

// Error:  '_x' is inaccessible due to 'private' protection level

Yes, it works. Unfortunately it made the code a bit hard to follow, which defeats the original purpose. I'll consider to move the init() to the same file. Thanks.

1 Like

You're welcome, glad I could help

Yeah you should generally put the init in the same file as the type declaration unless you have a really good reason not to (in my opinion). An example of when I might have the init in a separate file is when I'm modularising my code and I want to for example add conformance to Codable for multiple types in one spot to separate caching functionality from the types themselves. Hope that guidance helps

2 Likes

The underscored instance property for wrappers is always private.

That's the one!

As such, this is the general workaround form:

// File 1
extension Foo {
  init(x: DummyWrapper<Int>) {
    _x = x
  }
}

// File 2
extension Foo {
  init() {
    self.init(x: .init(wrappedValue: 1))
  }
}

@anon9791410 it can be simpler. Below is what @stackotter suggested:

// File 1
// Don't define init() explicitly. Just use the default member-wise init().

// File 2
extension Foo {
    init() {
        self.init(x: 1)
    }
}

It's simpler because it takes advantage of the small feature mentioned here.

1 Like

That's not the same thing. That's a subset of the general form—for when you don't need to pass in wrappers.

1 Like

The reason that I specified the self.init(x: x) form is because I was answering the question in general for any code that gets that error 'self' used before all stored properties are initialized. I believe that that error would happen even if Foo didn't have any private properties. And it's also not specific to property wrappers.