Using a dictionnary containing dictionnaries as values ex {UInt32:{UInt32:String}} may be quite harmful
Please find the code below explaining the problem and a suggested solution
pub fun main() {
let dictionnary:{UInt32:{UInt32:String}} = {}
dictionnary[0] = {1 : "Value1", 2 : "Value2"}
// Following code: Won't work and throw "cannot index into value which has type: `{UInt32: String}?`"
let test2 = dictionnary[0][2]
log(test2)
// Following code: Won't work and throw "cannot index into value which has type: `{UInt32: String}?`"
// Force Unwrap operator ! will not work here, has it is applied on second index in the inner dictionnary
let test2 = dictionnary[0][2]!
log(test2)
// Following code: Will work but :
// dictionnary[0] has to be fully copied to a new constant and unwrapped
// may be time and resource consuming if dictionnary[0] contains many data
let test1 = dictionnary[0]!
log(test1[2])
// Also for assiging new values you need to assing value to the unwrapped copy (test1) then assign the copy to the parent dictionnary
// Conclusion
// Using an inner dictionnary in a dictionnary may be Harmful has it requires a copy to access/set its values
// Proposed Solution
// Update the operator unwrap ! to cascade unwrap
}
To solve this problem, I think Cadence should update its fundamental structures.
All the problem start from here: the values in dictionaries are all optionals.
Optional is a really powerful concept in Cadence, but it would be nice to have a new grammar something like !! which enables cascade unwrapping!
let test2 = (dictionnary[0]!)[2]! is the way, but the problem is this looks ugly if you code ugly. Normally letβs imagine:
let metadatas:{UInt32:{UInt32:String}} = {}
var packID = 0
var NAME = 1
var DESCRIPTION = 2
metadatas[itemID] = {NAME : "Some Name", DESCRIPTION : "Some Description"}
it turns out:
fun getProperty(itemID, property): String{
var metadata = metadatas[itemID] ?? panic("Item not found)
var value = metadata[property] ?? panic("Item does not have the property)
return value
}
I mean in my opinion, in most cases if you need cascade unwrapping you are writing ugly code.
Thank you! But I donβt know why your are calling ugly coding the need for using Dictionnary of Dictionnaries. This use is not a question of coding, but just a question of functional requirements β¦ We are not all coding only for the simple metadata / PinataParty examples. Data structures representing Trees are more than basics in many fields. And very efficient for saving ressources and storage compared to linear collections.
I mean it is not ugly to use Dictionary of Dictionaries. even you can use Dictionary of Dictionary of Dictionaries.
Problem is accessing them:
dictionary[0][2][1] etc part is ugly.
I compared:
fun getProperty(itemID, property): String{
var metadata = metadatas[itemID] ?? panic("Item not found)
var value = metadata[property] ?? panic("Item does not have the property)
return value
}
ve
fun getProperty(itemID, property): String{
return (metadatas[itemID]!)[property]!
}
cadence runs like Exception based programming (with panics)
It is good practice to catch your exceptions and behave accordingly.
Oh ok I do understand but you have here a paradox:
Here you donβt know about what will cause the exceptions but the code is very very efficient:
fun getProperty(itemID, property): String{
return (metadatas[itemID]!)[property]!
}
Here you know how to track the exception but your code needs to copy in memory all the content of metadatas[itemID], with your line here
var metadata = metadatas[itemID] ?? panic("Item not found)
if metadatas[itemID] contains a lot of entries, and yous metadatas a lot of Items it is a nightmare to execute, you can try, you will see a huge difference in the execution duration.
May be the best solution to let the operator ?? cascading through sub Dictionnaries
var metadata = metadatas[itemID][property] ?? panic("Item not found)