I have a snippet of code like below:
protocol SomeProtocol {
}
struct SomeObject: SomeProtocol {
}
struct Test {
var arr: [[SomeProtocol]]
mutating func testFunction(objs:[[SomeObject]]) {
self.arr = objs
}
}
As you can see, SomeObject confirm the protocol, but the compiler shows me an
error " cannot assign value of type '[[SomeObject]]' to type '[[SomeProtocol]]'".
Can somebody tell me the reason?
Thanks a lot!
Because [[SomeObject]] is not the same type as [[SomeProtocol]] - see #TeeJay's comment above. The how is solved like this:
protocol SomeProtocol {
}
struct SomeObject: SomeProtocol {
}
struct Test {
var arr: [[SomeProtocol]]
mutating func testFunction(objs:[[SomeObject]]) {
self.arr = []
for a in objs {
var innerArray = [SomeProtocol]()
for e in a {
innerArray.append(e)
}
self.arr.append(innerArray)
}
}
}
Or, as #MartinR pointed out, you could use map.
If I try to short-circuit the inner loop with
self.arr = []
for a in objs {
self.arr.append(a)
}
I get the error "Cannot convert value of type 'Array<SomeObject>' to expected argument type [SomeProtocol]", which, on one level, is entirely correct - they are not the same - it's the elements, not the collection, which conform to the protocol. Array assignment doesn't dig down to the final element types for protocol conformance, unless you do it explicitly.
This is not entirely surprising - you wouldn't expect the following to work:
struct SomeOther1 {
var a : SomeProtocol
}
struct SomeOther2 {
var a : SomeObject
}
let x = SomeOther2(a: SomeObject())
let y: SomeOther1 = x // ERROR
whilst this should (and does) work:
let x = SomeOther2(a: SomeObject())
let y = SomeOther1(a: x.a)
Since Arrays are Structs that use generics, we can try the same using generics:
struct SomeOther<T> {
var a: T
}
let z = SomeOther<SomeObject>(a: SomeObject()) // OK, of course
let w = SomeOther<SomeProtocol>(a: SomeObject()) // OK, as expected
let v: SomeOther<SomeProtocol> = z // Doesn't work - types are not the same.
Related
I have code like this:
class A{}
class B: A{
var val = 1
}
class C: A{
var num = 5
}
extension Optional where Wrapped == [B?]{
var vals: [B]{
var result = [B]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
}
extension Optional where Wrapped == [C?]{
var vals: [C]{
var result = [C]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
}
var one: [B?]? = [B()]
var two: [C?]? = [C(), nil]
print(one.vals.count)
print(two.vals.count)
Here is the optimized one:
Combined into one, for B ( A's subclass ) & C ( A's subclass )
extension Optional where Wrapped: Collection{
var vals: [A]{
var result = [A]()
if let arr = self{
for part in arr{
if let val = part as? A{
result.append(val)
}
}
}
return result
}
}
Now question comes,
for case like the follwing,
how to go on the optimization?
print(one.vals.first?.val ?? "")
print(two.vals.first?.num ?? "")
I guess, I need a function to return an object's real type
PS: I know , to handle data , struct is perfect with protocol
While it's a company project, & I'm a new one
You need to introduce an extra type variable to say that the extension works on Optionals where Wrapped.Element is another Optional of any type. You have to express the "any type" part with another type variable, but you cannot add this type variable in the extension's declaration (though this feature is being proposed), or the property's declaration. What you can do instead, is to make vals a function:
func vals<T>() -> [T] where Wrapped.Element == T? {
var result = [T]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
Note that this can be simplified to:
extension Optional where Wrapped: Sequence {
func vals<T>() -> [T] where Wrapped.Element == T? {
self?.compactMap { $0 } ?? []
}
}
Just for fun. Another possible approach to keep it as a computed property instead of a generic method is to create an AnyOptional protocol with an associatedtype Wrapped and conform Optional to it. Then you can create a computed property to return an array of its Wrapped Element Wrapped type:
protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}
extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}
extension AnyOptional where Wrapped: Sequence, Wrapped.Element: AnyOptional {
var elements: [Wrapped.Element.Wrapped] {
optional?.compactMap(\.optional) ?? []
}
}
print(one.elements) // "[B]\n"
print(two.elements) // "[C]\n"
print(one.elements.first?.val ?? "") // "1\n"
print(two.elements.first?.num ?? "") // "5\n"
I have a protocol FooProtocol. and a class Bar<Foo:FooProtocol>. Inside a class an Array var mess: [Foo?]? to keep [foo1, foo2, nil, foo3...] or nil
And I try to make extension for this array to count new Foo object. I prefer to have protocols, because Foos could be very different objects delivered from outer world.
protocol FooProtocol {
....
init(from heaven: Int)
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional,
Wrapped.Element.Wrapped: FooProtocol // 'Wrapped' is not a member type of 'Wrapped.Element'
{
var united: Wrapped.Element.Wrapped { // Nope
let i = ...
return Wrapped.Element.Wrapped(from: i) // Nope
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
var important: Foo {
return mess.united
}
}
Any ideas? I'm blocked.
Edit 1:
After Leo suggestions I changed some parts of my code. But still stucked. This time more code from Playgrounds.
Any object that could be converted into '[Double]'. Could be color (as RGBA), Bezier curve, square, whatever...
public protocol FooProtocol {
var atomized: () -> [Double] {get}
static var count: Int {get}
init(_ array:[Double])
init()
}
public extension Array where Element: FooProtocol {
var average: Element {
var resultAtoms: [Double] = []
let inputAtoms = self.map {$0.atomized()}
for i in 0..<Element.count {
let s = inputAtoms.reduce(into: 0.0, {$0 += $1[i]}) / Double (Element.count)
resultAtoms.append(s)
}
return Element(resultAtoms)
}
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional<FooProtocol>
{
typealias Foo = Wrapped.Element.Wrapped // Doesn't work. How to get class?
var average: Foo { // I cannot use Wrapped.Element, it's Optional
if let thatsList = self {
let withOptionals = Array(thatsList) // OK, its [Optional<FooProtocol>]
let withoutOptionals = thatsList.compactMap({$0}) // OK, its [FooProtocol]
// This is funny, called from class works and makes 'bingo'.
return withoutOptionals.average // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
} else {
return Foo() // Hello? init Wrapped? Foo? How to get Foo()?
}
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
func workOn() {
let z:Foo = mess.average // OK, I can make 'mess.average ?? Foo()' but prefer not do it
}
// Thats OK
func workHard() { // To prove 'Array extension where Element: FooProtocol' works
if let messExist = mess {
let withoutOptionals = messExist.compactMap({$0})
let bingo = withoutOptionals.average //It's OK
}
}
}
class SomeFoo : FooProtocol {
static var count = 3
required init() {
a = 0
b = 0
c = 0
}
required init(_ array: [Double]) {
self.a = Int(array[0])
self.b = Float(array[1])
self.c = array[2]
}
var atomized: () -> [Double] {
return {return [Double(self.a), Double(self.b), self.c]}
}
var a: Int
var b: Float
var c: Double
}
let aFoo = SomeFoo([1, 2, 3])
let bFoo = SomeFoo([7, 9, 1])
let cFoo = SomeFoo([2, 6, 5])
let barData = [nil, aFoo, nil, bFoo, cFoo]
let barWithData = Bar(with: barData)
let barWithoutData = Bar<SomeFoo>(with: nil)
Maybe I should forget about extending array and make some functions inside a class (I'm almost sure I will need those functions somewhere else)
Edit 2
Even if I try to simplify and to make extension for Array I found troubles.
extension Array where
Element == Optional<FooProtocol>
{
func averageNils <Foo: FooProtocol>() -> Foo {
let withOptionals = Array(self) // OK, its [Optional<FooProtocol>]
let withoutOptionals = self.compactMap({$0}) // OK, its [FooProtocol]
return withoutOptionals.average as! Foo // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
}
}
From my understanding, it should work as you did, but one never knows what happens in the swift compiler world (and especially it's error messages).
Anyway, you can circumvent digging deeper into Wrapped.Element.Wrapped by specifyig the Wrapped.Element more precisely to be an Optional<FooProtocol>:
protocol FooProtocol {}
class Foo : FooProtocol {}
extension Optional where
Wrapped: Collection, //OK
Wrapped.Element == Optional<FooProtocol> // still good
{
var unfied: Wrapped.Element // Should be 'Foo' if self is '[Foo?]?' {
{
return 1 == 0 ? nil : Foo()
}
}
I try to break, count and later join objects inside another class.
So I build protocol:
typealias DataBreaker<T> = () -> [Double]
typealias DataJoiner<T> = (_ particles: [Double]) -> T
protocol SpaceIntepolatable {
associatedtype Atom
var breaker:DataBreaker<Atom> {get}
static var joiner:DataJoiner<Atom> {get}
}
and build an extension for Point:
extension Point:SpaceIntepolatable {
typealias Atom = Point
var breaker:DataBreaker<Atom> {
return {
return [self.x, self.y]
}
}
static var joiner:DataJoiner<Atom> {
return {particles in
return Atom(x: particles[0], y: particles[1])
}
}
}
Till now is fine. I can break Point into an Array<Double>
let particles = atom.breaker()
but joining
let newAtom = Atom.joiner(particles)
causes a compiler error:
Cannot convert value of type 'Atom.Atom' to specified type 'Atom'
It's probably because joiner is a static. But how to avoid it and get Atom as a result?
You have to call it on Point when you are outside the scope of the class.
let newAtom = Point.joiner(particles)
Edit:
You say that you have a generic class that looks like this:
class Space<Atom: SpaceIntepolatable> {
func test() {
let particles: [Double] = [0, 1]
let newAtom: Atom = Atom.joiner(particles)
}
}
Now, the problem is that the type of newAtom is incorrect. The SpaceIntepolatable protocol doesn't specify that Point and Point.Atom are the same type. Therefore Atom (Point) and Atom.Atom (Point.Atom) are not considered the same. What we want is Atom.Atom. Or we can just let the type to be inferred:
let newAtom: Atom.Atom = Atom.joiner(particles)
let newAtom = Atom.joiner(particles)
In general it is advisable not to reuse type names because then you get things like Atom.Atom. Maybe you actually want something like this:
protocol SpaceIntepolatable {
var breaker: DataBreaker<Self> { get }
static var joiner: DataJoiner<Self> { get }
}
and lose the Atom typealias completely, then:
class Space<Atom: SpaceIntepolatable> {
func test() {
let particles: [Double] = [0, 1]
let newAtom: Atom = Atom.joiner(particles)
}
}
will actually work.
I am trying to have nil for zeroth index element and rest will have value of Generic type T which is Comparable.
So when I initialise it will nil it works as expected
struct Container<T: Comparable> {
var container = [T?]()
init() {
container.append(nil)
}
}
but when I do it with an integer say 0, I get ambiguous reference
struct Container<T: Comparable> {
var container = [T?]()
init() {
container.append(0)
}
}
Playground execution failed: error: Algorithms.playground:7:9: error: ambiguous reference to member 'append'
container.append(0)
^~~~~~~~~
I want to understand why this error is occurring?
The issue is that T: Comparable does not necessarily mean Int, it means ANY value that conforms to Comparable, which includes String, Float, Bool, and the thousands of custom structs other programmers have written for their own projects.
Since 0 is not a valid value for all of these Structs, you essentially have one of two issues:
1) Do I actually just want the Container to always use Int - in which case:
struct Container
{
var container: [Int?]
init()
{
container = [nil]
}
}
var a = Container()
a.container.append (0)
2) Why am I using 0, when I mean nil
struct Container<T: Comparable>
{
var container: [T?]
init()
{
container = [nil]
}
}
var a = Container<Int>()
a.container.append (0)
T can by any comparable type (String, a custom type, ...),
and initializing it from 0 is not generally
possible.
You could require that T can be created from an integer literal,
this covers all integer and floating point types:
struct Container<T: Comparable> where T: ExpressibleByIntegerLiteral {
var container = [T?]()
init() {
container.append(0)
}
}
Or provide a separate append() method:
struct Container<T: Comparable> {
var container = [T?]()
init() {
container.append(nil)
}
mutating func append(_ newElement: T) {
container.append(newElement)
}
}
var c = Container<Int>()
c.append(0)
I have following struct:
public protocol SuperModel {
// empty protocol
}
struct ModelOne: SuperModel {
struct SubModelOne {
var someVar: Double
var othervar: Double?
}
var sub: SubModelOne?
mutating func setSub(sub: SubModelOne) {
self.sub = sub
}
}
In my class, I want to use this struct like that:
final class SomeClass: SuperClass {
var data: SuperModel
init() {
self.data = ModelOne()
}
func someFunc() {
(self.data as! ModelOne).setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
}
}
I get following error: Cannot use mutating member on immutable value of type 'ModelOne'. Why is that so and how can I fix this?
When you apply type casting to value types (such structs), if succeed, you receive immutable copy of requested value:
(self.data as! ModelOne) // this is copy of data
The only way (as known to me) how you can mutate values that need to be casted - reassign value (as #Sahil Beri pointed you need declare variable):
func someFunc() {
if var data = data as? ModelOne {
data.setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
self.data = data // you can do this since ModelOne conforms to SuperModel
}
}
Use like this,
struct UserAttributes {
var name:String?
var organizationID:String?
var email:String?
mutating func parseUserAttributes(attribues:[AWSCognitoIdentityProviderAttributeType])->UserAttributes{
for type in attribues{
if type.name == "name"{
name = type.value
}else if(type.name == "family_name"){
organizationID = type.value
}else if(type.name == "custom:role_id"){
role = type.value
}else if(type.name == "email"){
email = type.value
}
}
}
}
In some other file call like this,
var userAttributes = UserAttributes()
userAttributes = userAttributes.parseUserAttributes(attribues:attributes)
Problem is that you have declared data as SuperModel but allocate it as ModelOne. Declare data as ModelOne. Then the problem goes away.
final class SomeClass: SuperClass {
var data: ModelOne
init() {
self.data = ModelOne()
}
func someFunc() {
(self.data).setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
}
}
First downcast the self.data to ModelOne then call setSub function
if var data = self.data as? ModelOne {
data.setSub(ModelOne.SubModelOne(someVar: 2, othervar: 1))
}
#Shadow of is right. You try to mutate a temporary structure which is impossible and most of the time useless as it will be released once the mutation done. It's in fact a similar issue to trying to modify the return struct of a function. (see answer here : Cannot assign to property: function call returns immutable value)
In Swift 3, in my case, I was able to resolve the error just by changing struct to a class object.