Protocol 'Animal' can only be used as a generic constraint because it has Self or associated type requirements [duplicate] - swift

This question already has answers here:
Protocol can only be used as a generic constraint because it has Self or associatedType requirements
(6 answers)
Closed 5 months ago.
In swift5.x, I want to use a protocol named Animal with an associatedType 'T'.
class Shape {
func transform() {
print("Shape transofrm")
}
}
protocol Animal {
associatedtype T: Shape
var tansformT: T { get set }
func bark()
}
var a: any Animal
Rectangle and Circle implements the Shape protocol:
class Rectangle: Shape {
override func transform() {
print("Rectangle transofrm")
}
}
class Circle: Shape {
override func transform() {
print("Circle transofrm")
}
}
And there are two class implements Animal protocol:
class Dog : Animal {
var tansformT: Rectangle = Rectangle()
func bark() {
}
}
class Cat: Animal {
var tansformT = Circle()
func bark() {
print("Cat bark")
}
}
I want to declare a variable ani, it can be Dog or Cat according the condition, so i try this:
var a = 10
var ani: Animal
if a == 10 {
ani = Cat()
} else {
ani = Dog()
}
then the compiler reports an error:
Protocol 'Animal' can only be used as a generic constraint because it has Self or associated type requirements
I have try my best for 3+ hours, I don't know how to solve it.

Here I found an answer that can help you out.
https://stackoverflow.com/a/59330536/17286292
Failed with error:
Protocol 'Animal' can only be used as a generic constraint because it has Self or associated type requirements
var ani: Animal = Cat()
But change to opaque result type like this, it will work.
var ani: some Animal = Cat()
Doc: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html
From doc:
struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
The makeTrapezoid() function in this example declares its return type
as some Shape; as a result, the function returns a value of some given
type that conforms to the Shape protocol, without specifying any
particular concrete type. Writing makeTrapezoid() this way lets it
express the fundamental aspect of its public interface—the value it
returns is a shape—without making the specific types that the shape is
made from a part of its public interface.

Related

Are we able to nest functions that return protocol types now? Or was that always the case and the documentation is wrong?

I am working on a project with SwiftUI. And from the get-go, swiftUI shows the power and application of protocol oriented programming. So I started studying and expanding my knowledge on protocols and opaque types.
In the documentation The swift programming language towards the end of the page it clearly states:
problem with this approach is that the shape transformations don’t nest. The result of flipping a triangle is a value of type Shape, and the protoFlip(:) function takes an argument of some type that conforms to the Shape protocol. However, a value of a protocol type doesn’t conform to that protocol; the value returned by protoFlip(:) doesn’t conform to Shape. This means code like protoFlip(protoFlip(smallTriange)) that applies multiple transformations is invalid because the flipped shape isn’t a valid argument to protoFlip(_:)
However... when I try to nest the function, it works perfectly fine. So is the documentation wrong or is this a very recent update to the language and the documentation is a bit behind?
Here is the code (which I ran on playgrounds).
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
//return type is a protocol
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
//Testing if nested function works
let smallTriangle = Triangle(size: 3)
let testNest = protoFlip(protoFlip(smallTriangle))
I think that the book is using a bad example to illustrate the problem. In general your code will compile and run correctly. But some swift features like associated types and self requirements in methods of protocol breaking the concept of polymorphism we used to.
To summarise what I'am talking about take a look at this code:
protocol Shape: Equatable {
func draw() -> String
func doSomething(with other: Self) // self requirement
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
func doSomething(with other: Self) {
print("Tri size: \(other.size)")
}
}
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
func doSomething(with other: Self) {
print("Other filled shape: \(other.draw())")
}
}
struct Rect: Shape {
var width: Int
var height: Int
func draw() -> String {
let line = String(repeating: "*", count: width)
let result = Array<String>(repeating: line, count: height)
return result.joined(separator: "\n")
}
func doSomething(with other: Self) {
print("W: \(other.width) H:\(other.height)")
}
}
func protoFlip<T: Shape>(_ shape: T) -> Shape { //Compiler emits error: Use of protocol 'Shape' as a type must be written 'any Shape'
return FlippedShape(shape: shape)
}
let smallTriangle = Triangle(size: 3)
let protoFlippedTriangle = protoFlip(protoFlip(protoFlip(smallTriangle)))
print(protoFlippedTriangle.draw())
This code won't compile without opaque types because in this case compiler need to check if the shape we passed to functions is the same as we returned. Because we can not call doSomething on any other Shape other then the shape of the same type

why cant i return opaque types in my swift code? i am already returning a type conforming to that protocol [duplicate]

This question already has answers here:
What is the "some" keyword in Swift(UI)?
(14 answers)
Closed 1 year ago.
image
As showing in the image, i'm already using types such as MilkChocolate which conforms to the "Chocolate" protocal, and im trying to declare a function which returns a "some chocolate", which means return a type of chocolate depends on the input, but it gives me an error says
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
this confused me by a lot and i think MiklChocolate is an underlying type for the Chocolate protocol
whats wrong?🤔
and my code should match this tutorial: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html
PS: if you cant view the image, here's the code
protocol Chocolate {
associatedtype Content
var mass:Double {get}
}
struct Honey {}
struct Milk {}
struct Wine {}
class HoneyChocolate: Chocolate {
typealias Content = Honey
var mass = 1.0
}
class MilkChocolate: Chocolate {
typealias Content = Milk
var mass = 1.2
var mysteriousEffect = true
}
class WineChocolate: Chocolate {
typealias Content = Wine
var mass:Double = 999
}
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return HoneyChocolate()
case "milk":
return MilkChocolate()
default:
return WineChocolate()
}
}
The problem occurs because Swift can't return different types of generic types. HoneyChocolate and WineChocolate are different types and that causes an issue.
A different way of achieving your goal is to return a class type of Chocolate.
First let's declare our enum flavors:
enum Flavor {
case Honey, Milk, Wine
}
then we will declare the superclass Chocolate:
class Chocolate {
var flavor: Flavor
var mass:Double
init(_ flavor: Flavor ,_ mass: Double) {
self.flavor = flavor
self.mass = mass
}
}
Now we can inherit this class:
class HoneyChocolate: Chocolate {
init() {
super.init(Flavor.Honey, 1.0)
}
}
class MilkChocolate: Chocolate {
init() {
super.init( Flavor.Milk, 1.2)
var mysteriousEffect = true
}
}
class WineChocolate: Chocolate {
init() {
super.init( Flavor.Wine, 999)
var mysteriousEffect = true
}
}
finally we can return our chocolates:
func giveMeChocolate(of type:Flavor) -> Chocolate {
switch type {
case Flavor.Honey:
return HoneyChocolate()
case Flavor.Milk:
return MilkChocolate()
default:
return WineChocolate()
}
}
Why some doesn't work
some allows you to return a generic type like and ONLY one type.
for example:
let's make a CandyChocolate class:
class CandyChocolate: Chocolate {
typealias Content = Candy
var mass = 1.0
var sweetness
init(sweetness: int) {
self.sweetness = sweetness
}
}
Now to return a some Chocolate we are allowed to return exactly one type of chocolate:
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return CandyChocolate(sweetness: 5)
case "milk":
return CandyChocolate(sweetness: 3)
default:
return CandyChocolate(sweetness: 8)
}
}

Base class static method returns type of subclass in Swift

I want to have a base class static method which returns an array of the subclass type. Here is my current implementation. It works, but I have a small problem with it.
class Animal {
required init() { }
public static func generateMocks<T: Animal>() -> [T] {
var mocks: [T] = []
// some implementation goes here...
for _ in 0..<10 {
mocks.append( T() )
}
//
return mocks
}
}
let myMockAnimals: [Animal] = Animal.generateMocks() // this gives me type [Animal]
class Dog: Animal {
// dog things
var isCute = true
}
let myMockDogs: [Dog] = Dog.generateMocks() // this gives me type [Dog]
print(myMockDogs.first?.isCute) // true
/* My problem is that it is very annoying to have to declare my
myMockDogs variable as type "[Dog]". I would like it to
automatically infer this type. Like this: */
let myMockDogs2 = Dog.generateMocks() // oh no! It gives me type [Animal]
print(myMockDogs2.first?.isCute) // error! Value of type 'Animal' has no member 'isCute'
if let dog = myMockDogs2.first! as Dog { // error! 'Animal' is not convertible to 'Dog';
print(dog)
}
So my generic static function generateMocks is able to return the correct subclass when I specify the type of object I am expecting, like let myMockDogs2: [Dog] =..., but when I drop the explicit type I am expecting like in let myMockDogs2 =... then it suddenly goes back to using Animal as the type used for the generic function resulting in an array [Animal].
Is there a way to modify the generateMocks function which would cause let myMockDogs2 = Dog.generateMocks() to use type Dog for the generic T automatically?
You can copy this code into a Playground too! It works there! I'm really scared there's no solution to this, but maybe a Swift genius out there has an idea.
A possible solution is a protocol extension
protocol Animal {
init()
static func generateMocks() -> [Self]
}
extension Animal {
static func generateMocks() -> [Self] {
var mocks: [Self] = []
// some implementation goes here...
for _ in 0..<10 {
mocks.append( Self() )
}
//
return mocks
}
}
struct Dog: Animal {
// dog things
var isCute = true
}
let myMockDogs = Dog.generateMocks()
print(myMockDogs.first?.isCute) // true

Sets of a given size as a Swift type

In Swift, is there a way to construct a type of “sets of a given size?”(Despite the absence of dependent types in Swift, is such a construction nonetheless possible without excessive “contortions?”)
As an example, I would like to be able to define a parameterized type SetOfSizeTwo<T> whose instances are sets comprising of exactly two objects of (Hashable) type T.
Currently, I am using a poor man’s proxy:
struct SetOfSizeTwo<T> where T: Hashable {
var set = Set<T>(minimumCapacity: 2)
}
However, this type does not force the property set to be of size of 2.
Update
The blog article A hack for fixed-size arrays in Swift, by Ole Begemann, leads me to believe that a robust construction of a fixed-sized-set type in Swift 4 is non-trivial, if at all possible.
Here is one approach, as closest as I could get, to suit your needs. It is a bit rough around the edges and needs some polishing, but I think you'll get the idea:
class Size {
let size: Int
init(size: Int) {
self.size = size
}
required init() {
self.size = 0
}
}
class SizeOne: Size {
private override init(size: Int) {
super.init(size: size)
}
required init() {
super.init(size: 1)
}
}
class SizedSet<S, T> where S: Size, T: Hashable {
private var set: Set<T>
private let maximumSize: S
init() {
set = Set<T>()
maximumSize = S()
}
func insert(item: T) {
if !set.contains(item) && set.count + 1 <= maximumSize.size {
set.insert(item)
}
}
func remove(item: T) {
set.remove(item)
}
func contents() -> Set<T> {
return set
}
}
Usage:
let set: SizedSet<SizeOne, Int> = SizedSet()
print(set.contents())
// []
set.insert(item: 1)
print(set.contents())
// [1]
set.insert(item: 2)
print(set.contents())
// [1]

Is there some workaround to cast to a generic base class without knowing what the defined element type is?

I am trying to achieve a design where I can have a base class that has a generic property that I can change values on by conforming to a protocol.
protocol EnumProtocol {
static var startValue: Self { get }
func nextValue() -> Self
}
enum FooState: EnumProtocol {
case foo1, foo2
static var startValue: FooState { return .foo1 }
func nextValue() -> FooState {
switch self {
case .foo1:
return .foo2
case .foo2:
return .foo1
}
}
}
enum BarState: EnumProtocol {
case bar
static var startValue: BarState { return .bar }
func nextValue() -> BarState {
return .bar
}
}
class BaseClass<T: EnumProtocol> {
var state = T.startValue
}
class FooClass: BaseClass<FooState> {
}
class BarClass: BaseClass<BarState> {
}
Is it possible to end up with a solution similar to this where the element type is unknown and the value relies on the nextValue() method.
let foo = FooClass()
let bar = BarClass()
if let test = bar as? BaseClass {
test.state = test.state.nextValue()
}
This works but BarState will be unknown in my case and a lot of classes will be subclasses of BaseClass and have different state types.
let bar = BarClass()
if let test = bar as? BaseClass<BarState> {
test.state = test.state.nextValue()
}
This is a simplified example. In my case I will get a SKNode subclass that has a state property that is an enum with a nextvalue method that have defined rules to decide what the next value will be. I am trying to have a generic implementation of this that only relies on what is returned from the nextValue method. Is there a better pattern to achieve this?
This will not work for this exact scenario because EnumProtocol can not be used as concrete type since it has a Self type requirement, however, in order to achieve this type of behavior in other cases you can create a protocol that the base class conforms to and try to cast objects to that type when you are trying to determine if an object is some subclass of that type.
Consider the following example
class Bitcoin { }
class Ethereum { }
class Wallet<T> {
var usdValue: Double = 0
}
class BitcoinWallet: Wallet<Bitcoin> {
}
class EthereumWallet: Wallet<Ethereum> {
}
let bitcoinWallet = BitcoinWallet() as Any
if let wallet = bitcoinWallet as? Wallet {
print(wallet.usdValue)
}
This will not work, due to the same error that you are referring to:
error: generic parameter 'T' could not be inferred in cast to 'Wallet<_>'
However, if you add the following protocol
protocol WalletType {
var usdValue: Double { get set }
}
and make Wallet conform to that
class Wallet<T>: WalletType
then you can cast values to that protocol and use it as expected:
if let wallet = bitcoinWallet as? WalletType {
print(wallet.usdValue)
}