Brett Chapin
Brett Chapin

Reputation: 95

Why am I getting the error 'self' used before self.init call in my struct?

I've built this struct to handle a specific type of data I plan on using in some custom classes.
My issue is that featureSubSet variable is capable of being one of a few enums, and when this struct is initialized it wont know which enum it will be, so I declared it as Any.

When the public init is called, it will appropriately funnel the data to the needed private init method so that it can properly and fully be initialized.

I get the error at the end of the public init method, but I'm not sure how to get it to go away.

struct Feature {
//MARK: Variables needed for Feature
var featureSet: FeatureType
var featureSubSet: Any
var effect: String
var active: Bool?
var skill: Skill?
var ability: Ability?

public init(base: String, sub: String, effect: String, skill: Skill? = nil, ability: Ability? = nil) {
    switch base {
    case featureCategoryList()[0]: // Character Features
        self.init(CharacterFeature: sub, effect: effect)
    case featureCategoryList()[1]: // Combat Features
        self.init(CombatFeature: sub, effect: effect)
    case featureCategoryList()[2]: //Skill Features
        guard let newSkill = skill else {
            print("No skill")
            return
        }
        self.init(SkillFeature: sub, effect: effect, skill: newSkill)

    default:
        print("Somehow you picked something not on the list.")
        break
    }
}

private init(CharacterFeature sub: String, effect: String) {
    self.featureSet = .CharacterFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.effect = effect
}

private init(CombatFeature sub: String, effect: String) {
    self.featureSet = .CombatFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.effect = effect
}

private init(SkillFeature sub: String, effect: String, skill: Skill) {
    self.featureSet = .SkillFeatures
    self.featureSubSet = CharacterFeatures.init(rawValue: sub)!
    self.skill = skill
    self.effect = effect
}

//MARK: Feature functions
func string() -> String {
    //TODO: Make a string output for what the feature is.
    return ""
}
}

Upvotes: 3

Views: 1959

Answers (2)

Paulo Mattos
Paulo Mattos

Reputation: 19349

Assuming featureCategoryList is a struct Feature function, your original error:

self used before self.init call

might be coming from this lines:

case featureCategoryList()[0]: 
    ...
case featureCategoryList()[1]:
    ...
case featureCategoryList()[2]:

since you are implicitly calling self before initialization.

After fixing the above error, you will also need to fix these as well:

self.init isn't called on all paths before returning from initializer

Check out Rob's answer for some great alternatives.

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299565

This is an incorrect approach. If you find yourself storing something as Any, you're almost certainly on the wrong path. It's difficult to explore this code because it defines so many other types, but looking at it, it's unclear why you've needed Any here. featureSubSet is always of type CharacterFeatures. All the values of an enum are the same "type."

You have an error here:

    case featureCategoryList()[2]: //Skill Features
        guard let newSkill = skill else {
            print("No skill")
            return
        }

And here:

    default:
        print("Somehow you picked something not on the list.")
        break
    }

This returns without initializing self (and is probably your issue). If you can fail, then you need to make this an optional initializer (init?) or a throwing initializer, or you need to call fatalError() or similar crashing method on error. You can't just return without initializing.

I highly recommend that you redesign this, however. I don't see any reason that the parameters should be strings. Rather than passing in "character" and looking that up in some table, have the caller pass .character or .combat as an enum (you already have the enum).

Note in Swift 3, enum cases should always begin with a lowercase letter.

It's a little hard to understand what's going on in your code, but it doesn't really look like it should be a struct at all. It looks like an enum. Something like this:

enum CharacterFeature {
    // Features for characters
}

enum CombatFeature {
    // Features for combat
}

enum Skill {
    // skills
}

enum Effect {
    // effects
}

enum Feature {
    case character(CharacterFeature, Effect)
    case combat(CombatFeature, Effect)
    case skill(Skill, Effect)
}

Alternately, you could use a struct here, especially if there are more things that are shared by all features:

struct Feature {
    enum Kind {
        case character(CharacterFeature, Effect)
        case combat(CombatFeature, Effect)
        case skill(Skill, Effect)
    }

    let kind: Kind
    let effect: Effect
}

The key point is that you should generally work in explicit types, not strings. If you find yourself needing a too many optionals, you're often doing something incorrectly.

Upvotes: 3

Related Questions