Chapter 3 Lesson 4 - Prioritizing nil
This lesson will be super short, but it’s an important concept to understand when thinking about the relationship between Cadence (what we’re good at), and your frontend client that is using your smart contract code.
Getter Functions
As we know, a “getter” function is something that reads data. This occurs inside of a script when we want to read some data from our contract.
Let’s say we have a contract like this:
pub contract Profiles {
pub let profiles: @{UInt64: Profile}
pub resource Profile {
pub let name: String
pub let luckyNumber: UInt64
init(_ name: String, _ luckyNumber: UInt64) {
self.name = name
self.luckyNumber = luckyNumber
}
}
pub fun createProfile(name: String, luckyNumber: UInt64) {
let profile: @Profile <- create Profile(name, luckyNumber)
self.profiles[profile.uuid] <- profile
}
}
Pretty simple, right? Now, let’s say we want to write a function to fetch a Profile
reference from the profiles
dictionary. There are two ways we could write this function:
pub fun getProfile(id: UInt64): &Profile? {
return &self.profiles[id] as &Profile?
}
or…
pub fun getProfile(id: UInt64): &Profile {
return (&self.profiles[id] as &Profile?)!
}
Although both of these are correct, the first one is actually much better. This is what I call “prioritizing nil
over a panic
.” The reason being that we want to handle the case where the profile does not exist rather than aborting the whole script.
Script Data
Another reason for writing the function this way is because we don’t want our script to return errors. Imagine we had written our getter function so that it does not return an optional, so our script looks like this:
import Profiles from 0x01
pub fun main(id: UInt64): &Profile {
return Profiles.getProfile(id: id)
}
In this case, if the profile does not exist, the client calling this script will actually receive an error, which is sort of strange, right? The script itself is performing perfectly fine, but we were not able to find the profile. So we should have a much better way of communicating to our client that the profile simply doesn’t exist by returning nil
:
import Profiles from 0x01
pub fun main(id: UInt64): &Profile? {
// assuming `getProfile` returns a `nil`
return Profiles.getProfile(id: id)
}
Conclusion
When writing a getter function, by default you should always return an optional rather than aborting the script.
Quests
Take a look at the
getSetData
function in the TopShot smart contract found here. Find a different line in that contract that calls the function, and explain why it is beneficial that the function returns an optional rather thanpanic
ing.Find a contract on Mainnet that has a getter function that returns an optional type. Explain the benefit of having it written that way. Make sure to paste the link and line #.