Klaas
Klaas

Reputation: 22763

How can I create a generic class that is constrainted to structs?

As the Swift Language Guide points out generic types can be constraint to classes or protocols like this:

public class MyGenericClass<T:Equatable> {
    func printEquality(a:T, b:T) {
        if a == b {
            print("equal")
        } else {
            print("not equal")
        }
    }
}

Can I somehow restrict T to be a struct?

My use case is an observer class for value types that should only be used by structs.

As a side note: I know that there are e.g. class-only protocols, that are only implementable by classes. This is not really related, but shows that there is sometimes a special way to reach a goal.

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // class-only protocol definition goes here
}

Upvotes: 2

Views: 771

Answers (2)

Vatsal
Vatsal

Reputation: 18181

You can't (as of Swift 2.2).

It's simply not possible. There's no struct version of AnyObject, i.e. an AnyValue protocol (automatically implemented for all value types).

I've faced this exact problem myself, and I'm sorry to say that there's no solution other than to strictly abide by an informal protocol. Here's to hoping Swift 3 solves this.

Upvotes: 2

dfrib
dfrib

Reputation: 73186

The other answer answers this well (i.e.: you can't (yet?)).

I thought I'd add that you can, however, mimic at least the behaviour of this using runtime introspection and failable initializers; such that initialization of your generic class will succeed only if T is a structure (conforming to Equatable).

public class MyGenericClass<T: Equatable> {
    var foo: T

    init?(bar: T) {
        foo = bar

        if Mirror(reflecting: foo).displayStyle != .Struct {
            return nil
        }
    }
}

struct Foo : Equatable {
    let foo = 1
}
func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs.foo == rhs.foo }

/* Example */
if let _ = MyGenericClass<Int>(bar: 1) {
    print("Integer")
}
if let _ = MyGenericClass<Foo>(bar: Foo()) {
    print("Foo-Struct")
}
// prints only "Foo-Struct"

You could interpret failed initialization as non-conformance of your generic T to "only allow T to be structures", and possibly use optional binding (/...) to use this with your observer class in practice.

Upvotes: 1

Related Questions