readonly mutable fields in Swift - swift

When defining a class in Swift, you can have var properties which are like normal fields in other OOP languages, but also let properties which are both read-only and immutable (like T const * const in C++).
However is there a Swift equivalent of C++'s T * const? (That is, the field itself is immutable, but the object it points to is mutable)?
Here's a representation of my scenario:
class Foo {
let bar: Bar
init(bar: Bar) {
self.bar = bar
}
}
protocol Bar {
var fleem: Int? { get set }
}
class ConcreteBar : Bar {
var fleem: Int? = nil
}
var foo: Foo = Foo( bar: ConcreteBar() )
foo.bar.fleem = 123
(Playground link: https://iswift.org/playground?3jKAiu&v=2 )
Presently this gives me this compiler error:
Swift:: Error: cannot assign to property: 'bar' is a 'let' constant`
foo.bar.fleem = 123
Note that I am not actually setting bar, I'm only setting bar.fleem. I don't know why the compiler is complaining about assigning to bar.
If I change Foo to use this:
class Foo {
var bar: Bar
// ...
...then it compiles okay, but then I lose the guarantee that Foo.bar always has the same instance.
I know I could also change it to private(set):
class Foo {
public private(set) var bar: Bar
// ...
...but Foo itself is still free to overwrite the bar object-reference, and the use of var means that the compiler cannot assume the reference is immutable either, so some optimizations may be skipped.
I'm looking for something like a hypothetical let mutable or var readonly keyword or modifier.

By default, protocol typed objects have value value semantics. As a consequence, they're not mutable if the variable is a let constant.
To introduce reference semantics (and by extension, the mutability of objects referred to be a let constant), you need to make your protocol into a class protocol:
protocol Bar: class {
var fleem: Int? { get set }
}

You need to add the class attribute to the protocol to make it reference type compliant:
protocol Bar : class { ...

Related

Is there a way to set a typealias of a protocol's associated type in a sub protocol?

I didn't see this in the similar questions, so I think this is unique. I have a protocol
protocol Zot {}
protocol Foo {
associatedType Bar: Zot
var prop1: Bar { get set }
}
Now I could say
class Zoty: Zot {}
class Fooy: Foo {
typealias Bar = Zoty
var prop1: Zoty = Zoty()
}
But I haven't quite moved to the class layer yet. Is there a way for me to set the type alias in a protocol like so?
protocol Fooie: Foo {
typealias Bar = Zoty
var prop1: Zoty { get set }
}
The compiler says to make it like this
protocol Fooie: Foo where Bar == Zoty {
var prop1: Bar { get set }
}
But then it throws the error Cannot override mutable property 'prop1' of type 'Self.Bar' with covariant type 'Zoty'
To fix the error, just remove this line in Fooie
var prop1: Bar { get set }
You do not need to redeclare this again, and I do agree the error message is a bit confusing. Swift thinks that you are trying to override the property, which as far as it is concerned, is declared to be a different type, but in this context, the two types can only be the same.
After removing the property, you can now do:
class Fooy: Fooie {
var prop1 = Zoty()
}
and this gives errors
class SomethingElse: Zot {}
class Fooy: Fooie {
var prop1 = SomethingElse()
}
'Fooie' requires the types 'SomethingElse' and 'Zoty' be equivalent
Type 'Fooy' does not conform to protocol 'Fooie'

Marking member private results in Argument Passed to Call That Takes No Arguments

Compiles:
struct Foo {
var g: Double = 5.0
}
struct Bar {
var h: Double = 5.0
var foo = Foo()
}
var bar = Bar(h: 6)
Add "private" and no longer compiles, with above error in title:
struct Foo {
var g: Double = 5.0
}
struct Bar {
var h: Double = 5.0
private var foo = Foo()
}
var bar = Bar(h: 6) // compiler error on this line
Why would that be?
This is because for a struct a synthesised memberwise init will be private if any of its members are private. So instead you will get an init without arguments since all properties has default values (if at least one property that is non-optional lacks a default value no init will be synthesised at all).
From the Swift Programming Language book
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal.
So in your case you will need to write a custom init for your struct

Swift protocol with lazy property - Cannot use mutating getter on immutable value: '$0' is immutable

Goal: Create a protocol that allows lazy computation of a property for structs that conform to the protocol and then add the properties for an array of those structs. The computation is intensive and should only be executed once, hence the lazy requirement.
So, after lots of reading (eg this: Swift Struct with Lazy, private property conforming to Protocol) and trial and error (please don't flag this as a duplicate, unless it actually addresses this exact case), I came up with something that works:
import Foundation
protocol Foo {
var footype: Double { mutating get }
func calculateFoo() -> Double
}
struct Bar: Foo {
private lazy var _footype: Double = {
let value = calculateFoo()
return value
}()
var footype: Double {
mutating get {
return _footype
}
}
func calculateFoo() -> Double {
print("calc")
return 3.453
}
}
Testing this in a Playground:
var bar = Bar()
print(bar.footype)
print(bar.footype)
And the output is:
calc
3.453
3.453
So far, so good.
Now, I want to make an array of Bar and add the footype properties:
var bar1 = Bar()
var bar2 = Bar()
var bar3 = Bar()
var bars = [bar1, bar2, bar3]
print(bars.map { $0.footype }.reduce(0.0, +))
Which gives me the following error:
Cannot use mutating getter on immutable value: '$0' is immutable
Found a lot of info on this error, but am stuck how to solve it. One way would be to use class instead of struct, but that doesn't work well with other parts of the code.
Is it possible what I am trying to achieve?
As you can tell from the error message, $0 is immutable, so you can't use a mutating member on it.
Therefore, you can't iterate over the bars directly. What you can do is iterating through its indices, because we know that bars[$0] is mutable:
print(bars.indices.map { bars[$0].footype }.reduce(0.0, +))

Protocol only implemented by struct or immutable

I have a class A that holds an object B implementing the protocol P.
I would like to forbid any modification to object B without class A acknowledging it. Is it possible without any delegate or mutual reference?
Considering that it is possible to specify a protocol that can only be implemented by class type objects protocol P: class {} , if there is something similar for structs, I could bind the protocol to a struct, makeing it explicit (being known that structs are passed by value) that the object B has to be set but not edited.
Long story short:
Is there a way to force a protocol implementation to be a struct?
The only I would suggest is to wrap class A by a protocol Q and define a variable setter of an instance of protocol P inside.
protocol Q {
var p: P? { get set }
}
class A : Q {
var p: P? {
get {
// logic
}
set {
//logic
}
}
}
Protocols shouldn't be used this way. Protocols is to define behaviour, not the exact shape of object.
I assume by restricting protocol to structs you want to achieve immutability of it's implementers. If so we can design protocol with getters only
protocol Foo {
var foo: string { get }
}
This way Foo is immutable and it's can't be changed from anywhere no matter if it's struct or class.
Then, we can inherit FooMutable from Foo and add mutators there
protocol FooMutable: Foo {
var foo: string { get set }
}
Finally class A is the only place where we can mutate Foo:
class A {
private var fooValue: FooMutable = FooImpl()
var foo: Foo { get { return fooValue } }
func mutateFoo() {
fooValue.foo = "bar"
}
}
class FooImpl: FooMutable {
var foo = "foo"
}

Swift: implement a protocol variable as a lazy var?

It seems that it's not possible to implement a variable that's required by a protocol, with a lazy variable. For example:
protocol Foo {
var foo: String { get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
Compiler complains that Type 'Bar' does not conform to protocol 'Foo'.
It's also not possible to add the lazy keyword in the protocol declaration, since then you get 'lazy' isn't allowed on a protocol requirement error.
So is this not at all possible?
Citing the Language Guide - Properties - Lazy Stored Properties [emphasis mine]:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used.
I.e., the value is mutated upon first usage. Since foo has been blueprinted in the Foo protocol as get, implicitly nonmutating get, the value type Bar does not fulfil this promise with its lazy property foo, a property with a mutating getter.
Changing Bar to a reference type will allow it to fulfil the Foo blueprint (as mutating a property of a reference type doesn't mutate the type instance itself):
protocol Foo {
var foo: String { get }
}
class Bar: Foo {
lazy var foo: String = "Hello World"
}
Alternative, specify in the blueprint of the foo property of Foo that it has a mutating getter.
protocol Foo {
var foo: String { mutating get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
See the following Q&A for some additional details of the mutating/nonmutating specifiers for getters and setters:
Swift mutable set in property