11

If I have some existing struct, but I want to use "Reference" behavior, how do I achieve that?

I can write some simple class-holder like

class Box<T> {
    var value: T
    init(_ value: T) {
        self.value = value
    }
}

I guess there must be ready class in the standard library, but I didn't find it.

I want to store that reference in my class, so inout parameter isn't what I need.

vbezhenar
  • 11,148
  • 9
  • 49
  • 63
  • Would a `class` be more suitable for your purposes? They're passed by reference. Instances of `struct` are designed to be passed by value. – sapi Oct 18 '14 at 06:27
  • That would solve my issue, but that means that I have to duplicate existing framework structure like CGRect and implement conversions back and forth. I want to avoid duplication if possible. – vbezhenar Oct 18 '14 at 06:34
  • @sapi: Everything in Swift is always passed by value, unless the parameter is marked `inout` in which case it is always passed by reference. – newacct Oct 19 '14 at 03:58
  • @newacct Basically, `inout` parameter is not "call-by-reference", but rather ["call-by-copy-restore"](http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_copy-restore). – rintaro Oct 20 '14 at 04:08

3 Answers3

7

For me the best variant was using class-holder:

class Ref<T> {
  var value: T

  init(_ value: T) {
    self.value = value
  }
}
vbezhenar
  • 11,148
  • 9
  • 49
  • 63
6

You can, but you should not store it in your class.

struct Size {
    var width:Float
    var height:Float
}

class Rect {
    var sizeRef:UnsafeMutablePointer<Size>
    init(_ size:UnsafeMutablePointer<Size>) {
        self.sizeRef = size
    }
    func doubleSize() {
        self.sizeRef.memory.height *= 2.0
        self.sizeRef.memory.width *= 2.0
    }
}  

var size = Size(width: 20.0, height: 20.0)
let rect = Rect(&size)
rect.doubleSize()
println("size: \(size.width) x \(size.height)") // -> size: 40.0 x 40.0

Because, usually, struct is allocated from "stack" memory, when you do like this:

func makeRect() -> Rect {
    var size = Size(width: 20.0, height: 20.0)
    return Rect(&size)
}

let rect = makeRect()

rect.sizeRef no longer points valid memory. UnsafeMutablePointer is unsafe in a literal sense.

Bjorn
  • 69,215
  • 39
  • 136
  • 164
rintaro
  • 51,423
  • 14
  • 131
  • 139
1

I use this:

@propertyWrapper public struct Inout<T> {
    public init(wrappedValue: T) {
        storage = .init(value: wrappedValue)
    }
    
    private let storage: Storage
    
    public var wrappedValue: T {
        nonmutating get { storage.value }
        nonmutating set { storage.value = newValue }
    }
    
    private class Storage {
        init(value: T) {
            self.value = value
        }
        
        var value: T
    }
}

Example:

struct Example { 
    init(value: Inout<Int>) { 
        _value = value
    }
    
    @Inout var value: Int
    
    func performAction() { 
        value += 19
    }
}
let value = Inout(wrappedValue: 50)
let example = Example(value: value)
example.performAction()
print(value.wrappedValue) // 69
Arutyun Enfendzhyan
  • 1,612
  • 1
  • 12
  • 15