Constrained generic `T` behaves like `Self` - so what is currently `self` in my generic initializer here?

open class RecordEncoder: NSCoder {
    required public init(coder: NSCoder) {
        
    }
    public init?<T>(from: Record<T>) where T: NSCoder {
        return nil
    }
    public init(record: RecordEncoder.Type, song: String) {
        
    }
}
public struct Record<RecordType: RecordEncoder>: Sendable {
    var song: Data
    public init(record: RecordType.Type, song: Data) {
        self.song = song
        
    }
    
}

protocol SongDelegateProtocol {
    var didInit: RecordEncoder? { get set }
}
class RecordManager: SongDelegateProtocol {
    var didInit: RecordEncoder?
    lazy var songData = Data()
    static let shared = RecordManager()
    func initialize<T>(_ delegate: T.Type) -> T? where T:RecordEncoder {
        return T.init(from: Record<T>.init(record: T.self, song: songData))
    }
}
extension NSObjectProtocol where Self: RecordEncoder {
    var delegate: RecordManager? {
        RecordManager.shared.didInit = self
        return RecordManager.shared
    }
    public init?<T>(from songSnapshot: Record<T>) where T: RecordEncoder {
        print("Starting")
        RecordManager.shared.didInit = RecordManager.shared.didInit?.delegate?.initialize(T.self)
        print("Done")
        self.init(coder: .init())
    }
}

class RandomObject: RecordEncoder {
    
    required public init(coder: NSCoder) {
        print("one")
        super.init(record: RandomObject.self, song: "")
    }
    override func copy() -> Any {
        print("Copied one")
    }
}
class RandomObjectTwo: RecordEncoder {
    required init(coder: NSCoder) {
        print("two")
        super.init(record: RandomObjectTwo.self, song: "two")
    }
    override func copy()  -> Any {
        print("Copied two")
    }
}
class RandomObjectThree: RecordEncoder {
    required init(coder: NSCoder) {
        print("three")
        super.init(record: RandomObjectTwo.self, song: "three")
    }
    override func copy() -> Any {
        print("Copied three")
    }
}
class RandomObjects: XCTestCase {
    func test_Random_Objects_Who_All_Subclass() {
        guard let kpopSong = "Sweet Bomb!".data(using: String.Encoding.utf8) else {
            return XCTFail("Failed to unwrap the encoded data")
        }
        
        XCTAssertNotNil(RandomObject(from: Record<RandomObject>.init(record: RandomObject.self, song: kpopSong))?.copy(), "We failed to unwrap an instance of RandomObject")
        XCTAssertNotNil(RandomObjectTwo(from: Record<RandomObjectTwo>.init(record: RandomObjectTwo.self, song: kpopSong))?.copy(), "We failed to unwrap an instance of RandomObjectTwo")
        XCTAssertNotNil(RandomObjectThree(from: Record<RandomObjectThree>.init(record: RandomObjectThree.self, song: kpopSong))?.copy(), "We failed to unwrap an instance of RandomObjectTwo")
    }
}

Is Self.self just T.self ?

If I place the constraint on T as Self instead of RecordEncoder my compiler gets upset at me.

Then before I leave the initialzer, it chooses the right method..
How are T.self and Self.self different here?

The test passes, and line 50 calls into the appropriate subclass, per T.self no matter what it is given, so long as that object meets all of the constraints.

Is it (T) really different from Self ?

It seems that T.self is equivalent to Self.self here. Is that expected?

If T.self and Self.self are the same, then what is self here?

Is it ever the case here that T is not Self ? Because if that is true, then I can trust the compiler, but, until then I have my doubts.

I’m curious, what leads you to believe that T is the same as Self in this code?

The only constraints I see are Self: RecordEncoder and T: RecordEncoder. That’s basically like saying a dog must be an animal and a cat must be an animal—it doesn’t make a dog the same as a cat.

Good question - would comparing the value of the meta-type for T and Self be enough to confirm?

I have tried asserting inside of the initializer

and I have tried requiring that T be a Self - but the compiler is not ok with it

Hopefully I will figure out what I am missing here

In order to constrain T to be the same as Self, you have to write T == Self.

But then step back a second: why are you introducing a generic parameter T at all? When you write init?<T>(...) where T: RecordEncoder, you're saying that users can choose any T that's a RecordEncoder. If you constrain T == Self, that's like Henry Ford saying that customers can choose any color of Model T as long as it's black.

1 Like

Hmm I will go revisit the problem and if I have any more questions I’ll bump the thread

Edit: Does this mean that there can be a case in this initializer where T is not Self?

“any color of Model T as long as it's black”
Thanks for this - it’s funny :laughing: and it is making me think hard about why T is there in the first place :laughing: