I am looking to have a re-useable function in a struct that I can call in my init method. However, when I do this I get the following error.
'self' used before all stored properties are initialized
How can I use calculateValue
in my init
method without raising this error? I will be using the calculateValue
function in other parts of my struct so I don't want it to live in my init
function.
struct Card {
let face: Face
let suit: Suit
let value: Int
init(face: Face, suit: Suit) {
self.face = face
self.suit = suit
self.value = self.calculateValue(face: face)
}
private func calculateValue(face: Face) -> Int {
if face == Face.two { return 2 }
if face == Face.three { return 3 }
if face == Face.four { return 4 }
if face == Face.five { return 5 }
if face == Face.six { return 6 }
if face == Face.seven { return 7 }
if face == Face.eight { return 8 }
if face == Face.nine { return 9 }
return 10
}
}
CodePudding user response:
I can think of two possible solutions.
- Make
calculateValue
a static function. This works since it doesn't depend on any state.
struct Card {
let face: Face
let suit: Suit
let value: Int
init(face: Face, suit: Suit) {
self.face = face
self.suit = suit
self.value = Card.calculateValue(face: face)
}
private static func calculateValue(face: Face) -> Int {
if face == Face.two { return 2 }
if face == Face.three { return 3 }
if face == Face.four { return 4 }
if face == Face.five { return 5 }
if face == Face.six { return 6 }
if face == Face.seven { return 7 }
if face == Face.eight { return 8 }
if face == Face.nine { return 9 }
return 10
}
}
- Move the functionality to return a value to the
Face
enum. Here's a partial guess at yourFace
enum with a computedvalue
property:
enum Face {
case two
case three
case jack
var value: Int {
switch self {
case .two:
return 2
case .three:
return 3
default:
return 10
}
}
}
Then your Card
becomes:
struct Card {
let face: Face
let suit: Suit
let value: Int
init(face: Face, suit: Suit) {
self.face = face
self.suit = suit
self.value = face.value
}
}
Though now you don't even need the value
property or you can make it a computed property.
CodePudding user response:
My suggestion is to declare the enum with Int
raw value. Assigning the initial raw value is sufficient, the compiler infers the sequence
enum Face : Int {
case two = 2, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace
}
and in Card
get the value with a computed property which returns the raw value of face
if it's less than 10 otherwise 10.
struct Card {
let face: Face
let suit: Suit
var value: Int {
return face.rawValue < 10 ? face.rawValue : 10
}
}
The init method is for free.
CodePudding user response:
How about moving this into Face
itself, e.g…
enum Face {
case two, three, four, five, six, seven, eight, nine, ten, jack, queen, king
var value: Int {
switch self {
case .two: return 2
case .three: return 3
case .four: return 4
case .five: return 5
case .six: return 6
case .seven: return 7
case .eight: return 8
case .nine: return 9
default: return 10
}
}
}
then your init can be…
struct Card {
let face: Face
let suit: Suit
let value: Int
init(face: Face, suit: Suit) {
self.face = face
self.suit = suit
self.value = face.value
}
}
CodePudding user response:
Let me make another suggestion: Just make your Face
value have an Int
raw value:
enum Face: Int {
case ace = 1
case two = 2
case three = 3
case four = 4
case five = 5
case six = 6
case seven = 7
case eight = 8
case nine = 9
case ten = 10
case jack = 11
case queen = 12
case king = 13
}
At that point, you can just set self.value = face.rawValue
. However, you shouldn't do that, because you'd have a split source of truth. The Card
structs don't need a copy of the value. They can just access the value of the face, which they already contain:
struct Card {
let face: Face
let suit: Suit
init(face: Face, suit: Suit) {
self.face = face
self.suit = suit
}
var value: Int { self.face.rawValue }
}