How can I have a variable that stores a few different struct types?

Prerequisite Information


Hello,

I would like to know if there is some way to create a variable, that could store a few different struct types.

Example:

Code Block
struct TestOne {
var id: Int
var name: String
var colour: String
static var text: String = "TestOne"
}
struct TestTwo {
var id: Int
var lesson: String
var description: String
static var text: String = "TestTwo"
}
struct ChosenTest {
var test: testOne or testTwo
func printText() {
print(test.text)
}
}

I tried adding a protocol,

Code Block
protocol TestGroup {}

and then make the structs adhere to it and change it in the ChosenTest, like

Code Block
struct TestOne: TestGroup {
var id: Int
var name: String
var colour: String
static var text: String = "TestOne"
}
struct TestTwo: TestGroup {
var id: Int
var lesson: String
var description: String
static var text: String = "TestTwo"
}
struct ChosenTest {
var test: TestGroup
func printText() {
print(test.text)
}
}

When i try to create a ChosenTest struct and initialize the test variable, it fails because it wants the object of that type and not the actual TestOne.Type or TestTwo.Type

Questions

  • How could I make the TestOne.Type/TestTwo.Type conform to the ChosenTest protocol?

  • How could i do something like the code below (or achieve the same result), if the question above is not possible to be done?

Code Block
struct ChosenTest {
var test: TestGroup
func printText() {
print(test.text)
}
}

Answered by OOPer in 654709022

if I could have a variable inside a struct of type Tor TypeGroup that I could store inside any type of Test struct??

Something like this?:
Assuming you have TypeGroup and TypeOne in my last reply.
Code Block
struct TestStruct {
var testType: TypeGroup.Type = TypeOne.self
}
func printTest (testStruct: TestStruct) {
print(testStruct.testType.desc)
}
printTest(testStruct: TestStruct())
//->This is a String

: TypeGroup.Type represents that testType can hold any type objects which conforms to TypeGroup, and TypeOne.self is one of the type object conforming TypeGroup.
In Swift, type object is represented by .self after type name.
Unless I miss something, it looks like you want some inheritance on your struct. Why not use class instead ?

Code Block
class Test {
var id: Int = 0
}
class TestOne : Test {
var name: String = ""
var colour: String = "clear"
static var text: String = "TestOne"
}
class TestTwo : Test {
var lesson: String = ""
var description: String = "lesson"
static var text: String = "TestTwo"
}
struct ChosenTest {
var test1 = TestOne()
var test2 = TestTwo()
func printText() {
print(type(of: test1).text, "and", type(of: test2).text)
}
}
let testOne = TestOne()
let testTwo = TestTwo()
let chosen = ChosenTest(test1: testOne, test2: testTwo)
chosen.printText()

You get
TestOne and TestTwo

Note: you cannot declare the static var in Test, otherwise when you change in a subclass you change for all other subclasses
Or maybe you want this:

Code Block
class Test {
var id: Int = 0
}
class TestOne : Test {
var name: String = ""
var colour: String = "clear"
static var text: String = "TestOne"
}
class TestTwo : Test {
var lesson: String = ""
var description: String = "lesson"
static var text: String = "TestTwo"
}
struct UniversalTest {
var test : Test
func printText() {
if let tested = test as? TestOne {
print(type(of: tested).text)
}
if let tested = test as? TestTwo {
print(type(of: tested).text)
}
}
}
var testUniversal = UniversalTest(test: testOne)
testUniversal.printText()
testUniversal = UniversalTest(test: testTwo)
testUniversal.printText()

and get
TestOne
TestTwo


First of all, your ChosenTest would not compile even if you specified TestOne or TestTwo for test:
Code Block
struct TestOne {
var id: Int
var name: String
var colour: String
static var text: String = "TestOne"
}
struct ChosenTest {
var test: TestOne
func printText() {
print(test.text) //->Static member 'text' cannot be used on instance of type 'TestOne'
}
}


You have several options to fix this:
  1. Make text an instance property

  2. Change text to type(of: text) in printText()

  3. Change text to TestOne in printText()

Code Block
Ex1: do {
struct TestOne {
var id: Int
var name: String
var colour: String
let text: String = "TestOne" //<-
}
struct ChosenTest {
var test: TestOne
func printText() {
print(test.text)
}
}
}
Ex2: do {
struct TestOne {
var id: Int
var name: String
var colour: String
static var text: String = "TestOne"
}
struct ChosenTest {
var test: TestOne
func printText() {
print(type(of: test).text) //<-
}
}
}
Ex3: do {
struct TestOne {
var id: Int
var name: String
var colour: String
static var text: String = "TestOne"
}
struct ChosenTest {
var test: TestOne
func printText() {
print(TestOne.text) //<-
}
}
}

You may think #3 cannot be your option, but that depends on some other things.
Considering all possible combinations would take huge space, so I assume you can accept #1.
If you cannot, please leave me a comment.


This following code fails to compile:
Code Block
protocol TestGroup {}
struct TestOne: TestGroup {
var id: Int
var name: String
var colour: String
let text: String = "TestOne"
}
struct TestTwo: TestGroup {
var id: Int
var lesson: String
var description: String
let text: String = "TestTwo"
}
struct ChosenTest {
var test: TestGroup
func printText() {
print(test.text) //<- Value of type 'TestGroup' has no member 'text'
}
}


This is because Swift compiler does not know that TestGroup has an instance property named test.
You can tell Swift modifying the TestGroup protocol:
Code Block
protocol TestGroup {
var text: String {get}
}



If this is not what you want, please tell me.

Hello Claude31,

You are right, I would like to be able to have some inheritance on my structs. As for using classes, I just though I wouldn't be needing them and because the structs are a bit more safe and memory efficient, I used those.
So, if you want inheritance, just use class.

IMHO, you will never notice memory issue because of it, may even be reverse.

And passing objects by reference may avoid copy content, hence saving memory. But once again, that is not an issue here.

If you are interested in class vs struct comparison, there are tons of articles available.
Just one here:
https ://medium. com/@linhairui19/struct-vs-class-understanding-swift-performance-part-1-f46a6811c8f2
Hello OOPer,

What I want is some way to be able to group some structs and then, a function to be able to receive as argument one of the structs (preferably it's type and not it's instance) in that group, do some calculations with it and then return it, but in a way that the code that called this function, would be able to get the type it wants without returning Any and then have to cast each of the results to the expected type.

Example
(I know that my example is a bit weird and complicated, but i would like to do something like this if possible. It's kind of a dynamic variable type):

Code Block
struct typeOne: TypeGroup {
var id: Int = 0
var type: String = "test"
var general: String = "test"
static var descString: String = "This is a String"
}
struct typeTwo: TypeGroup {
var id: Int = 0
var number: Int = 0
var general: String = "test"
static var descNum: Int = 0
}
struct typeThree: TypeGroup {
var id: Int = 0
var kind: String = "test"
var general: String = "test"
static var descType: String = "This is a type"
}
func typeFunc(typee: TypeGroup) -> TypeGroup {
/* Something like this: */
result: typeOf(typee) = typee.init()
result.general = "Type: \( typee.descType)"
/* or this: */
var result = typee.init()
switch typeof(typee) {
case typeOne:
result.general = "String: \(typee.descString)"
break
case typeTwo:
result.general = "Number: \(5 + typee.descNum)"
break
case typeThree:
result.general = "Type: \( typee.descType)"
break
}
return result
}
/* Then be able to do this: */
var typeResult: typeOne = typeFunc(typee: typeOne.Type)
/* or this: */
var typeResult: typeTwo = typeFunc(typee: typeTwo.Type)


Example

Sorry, but your new example does not explain what you want to do any better for me.

I can write something similar in Swift:
Code Block
protocol TypeGroup {
init()
var general: String {get set}
static var desc: String {get}
}
struct TypeOne: TypeGroup {
var id: Int = 0
var type: String = "test"
var general: String = "test"
static let desc: String = "This is a String"
}
struct TypeTwo: TypeGroup {
var id: Int = 0
var number: Int = 0
var general: String = "test"
static let desc: String = "0"
}
struct TypeThree: TypeGroup {
var id: Int = 0
var kind: String = "test"
var general: String = "test"
static let desc: String = "This is a type"
}
func typeFunc<T: TypeGroup>(typee: T.Type) -> T {
var result: T = T()
result.general = "Type: \(T.desc)"
return result
}
var typeResult1: TypeOne = typeFunc(typee: TypeOne.self)
var typeResult2: TypeTwo = typeFunc(typee: TypeTwo.self)

Please tell me what is different than what you expect.
Hello OOPer,

Your last answer indeed answers most of what I wanted. I forgot about the usage of Generics.

My only remaining question which i am not sure if it has been answered, is if I could have a variable inside a struct of type T or TypeGroup that I could store inside any type of Test struct??

Example:

Code Block
struct TestStruct {
var testType: TypeGroup = TestOne.Type /* This fails because the TestOne struct instance conforms to TypeGroup, but not the TestOne Type */
}
func printTest (testStruct: TestStruct) {
print(testStruct.testType.desc)
}
printTest(testStruct: TestStruct())


Accepted Answer

if I could have a variable inside a struct of type Tor TypeGroup that I could store inside any type of Test struct??

Something like this?:
Assuming you have TypeGroup and TypeOne in my last reply.
Code Block
struct TestStruct {
var testType: TypeGroup.Type = TypeOne.self
}
func printTest (testStruct: TestStruct) {
print(testStruct.testType.desc)
}
printTest(testStruct: TestStruct())
//->This is a String

: TypeGroup.Type represents that testType can hold any type objects which conforms to TypeGroup, and TypeOne.self is one of the type object conforming TypeGroup.
In Swift, type object is represented by .self after type name.
Thanks for the answer!

That was what i was looking for, although
Code Block
testStruct.testType.desc

is not working, because the compiler does not know what kind of struct the testType is, and throws error: Value of type 'testType.Type' has no member 'desc', but I think I'll find a way around it.

although 

Code Block
testStruct.testType.desc

is not working, because the compiler does not know what kind of struct the testType is, and throws error: Value of type 'testType.Type' has no member 'desc', but I think I'll find a way around it.
All my codes shown are confirmed to work in my environment. I guess you are using another version of TypeGroup.
But I think you will find the right definition for it.
I found out why it was not working. I didn't add any variables inside the protocol, and so it could not find anything, and thus throwing the error.
How can I have a variable that stores a few different struct types?
 
 
Q