1

In the following example, you can see that the dictionary in the myStruct instance is not returned by reference in the getDictionary() function. Therefore, any changes made to the returned dictionary is only made to the copy. How can you return the dictionary by reference?

 struct myStruct {
        func getDictionary() -> [Int:String] {
            return dictionary
        }
    
        private var dictionary = [1:"one"]
    
    }
    
    
    let c = myStruct()
    var sameDict = c.getDictionary()
    sameDict[2] = "two"
    
    print(c.getDictionary(), sameDict)

[1: "one"] [1: "one", 2: "two"]

Rage
  • 870
  • 9
  • 27
  • You can do `c.dictionary = ...` or `c.dictionary[2] = "two"` – pawello2222 Aug 31 '20 at 21:03
  • @pawello2222 sure but that is not what I asked – Rage Aug 31 '20 at 21:20
  • What _did_ you ask? That is what you do: change something and assign back into the dictionary. – matt Aug 31 '20 at 21:35
  • 1
    It is Swift naming convention to name your protocols, structures and classes starting with an uppercase letter. – Leo Dabus Aug 31 '20 at 21:41
  • inefficient for large data sets @matt – Rage Aug 31 '20 at 21:42
  • 3
    How do you know? And why do you think `inout` is more "efficient"? Even with `inout` you are assigning a whole new dictionary every time you call this method. – matt Aug 31 '20 at 21:51
  • @matt I am not sure how familiar you are with C programming, but with inout the dictionary is passed by reference, meaning we are only passing a pointer to the dictionary. Any changes made to that dictionary are reflected inside the myStruct instance. Without inout, swift copies the dictionary each time the dictionary is passed into a function or returned, which is inefficient for large data sets. – Rage Aug 31 '20 at 22:55
  • 1
    Well yes but Swift is not C. It does not behave like C and your code is not talking to C. One can be imagining a certain kind of "efficiency" but without touching the reality of how Swift works, regardless of whether we use `inout` or set a value into the dictionary. – matt Aug 31 '20 at 23:32
  • @Rage Dictionaries are already returning their storage by pointer. They're a struct, but their main content is in a heap-allocated container that's referenced by pointer. The struct is being copied, but it's only 1 machine word in size. Abstract performance concerns are pointless. – Alexander Sep 01 '20 at 12:10

3 Answers3

2

Dictionary is a value type, it is not your choice to do some type of data structure to be reference or value, it is a Swift's choice. Only closure, class and functions can be used as reference

In Swift, Array, String, and Dictionary https://developer.apple.com/swift/blog/?id=10

  • So it is a dead end then? – Rage Aug 31 '20 at 21:20
  • Yes. They can’t be used as a reference. But actually you have an abstract task which calls like - i want to do it like reference. In a real app you don’t need to do it with dictionaries – Alexander Nikolaychuk Aug 31 '20 at 21:22
  • @Rage `struct` types can be passed by reference using `inout` keyword. Take a look at my answer. – gcharita Aug 31 '20 at 21:34
  • It can be modify as a inout, but it is not same as a reference – Alexander Nikolaychuk Aug 31 '20 at 21:36
  • @AlexanderNikolaychuk what is the difference? – gcharita Aug 31 '20 at 21:37
  • 2
    When you use an inout then, in the start of function you have a COPY of global variable, after modification of this copy inside the function, before the return, the modified copy WILL assign to the global variable. It is not a variable reference modification - it is FULL assignment. (Hope you understood what i am trying to say, sry for poor english) – Alexander Nikolaychuk Aug 31 '20 at 21:44
  • @AlexanderNikolaychuk well, didn't know that! I also search and just realized that no one says pass by reference, like this answer https://stackoverflow.com/a/50260961/6791677. Thanks. – gcharita Aug 31 '20 at 21:49
1

Because the Dictionary is a struct type, the only way to do this is by passing it in a function using inout keyword (indicates that the parameter will be changed) like this:

struct MyStruct {
    func getDictionary() -> [Int: String] {
        return dictionary
    }
    
    mutating func updateDictionary(block: (inout [Int: String]) -> Void) {
        block(&dictionary)
    }

    private var dictionary = [1:"one"]
}


var c = MyStruct()
c.updateDictionary {
    $0[2] = "two"
}

print(c.getDictionary())

Update: After modification of the copy inside the function, before the return, the modified copy WILL assign to the global variable. @AlexanderNikolaychuk and @matt pointed out that in the comments. The behavior can be seen if you run the following code in a Playground:

struct MyStruct {
    var some = 1
}

var myStruct = MyStruct() {
    didSet {
        print("didSet")
    }
}

func pass(something: inout MyStruct) {
    something.some = 2
    print("After change")
}

pass(something: &myStruct)

this will print:

After change
didSet

Just saying.

Kamil.S
  • 5,205
  • 2
  • 22
  • 51
gcharita
  • 7,729
  • 3
  • 20
  • 37
0

It seems that you did not quite understand the difference between struct and class.

When you initialise the struct and assign it to c you have your first copy of it. Then you initialise a new variable, calling it sameDict and copying the value of the c dictionary to it. Then you modify the copy called sameDict. The dictionary of c is still the same.

Check this doc: https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

Stuct's are passed around by copying them. classes get referenced.

Simon
  • 1,754
  • 14
  • 32