You’ve learned the basics of operator overloading in Chapter 16, “Protocols”, where you implemented the Equatable and Comparable protocols and added custom behavior to standard operators.
However, there are certain cases when overloading standard operators is simply not enough. This chapter will show you how to create custom operators from scratch and define your very own subscripts, a special case of computed properties. You’ll use subscripts to declare your own shortcuts for accessing the elements of custom types and provide keypaths as dynamic references for properties of objects.
Custom operators
You declare your own operators when you want to define custom behavior not covered by the standard operators. Think of exponentiation, for example. You could overload the multiplication operator since exponentiation means repeated multiplication. Still, it would be confusing. Operators should do only one type of operation, not two.
So you’ll define your own exponentiation operator, first only for a specific type, then extend it by making it generic. Before doing that, you need to know a little bit of theory about operator types. Time to dive in!
Types of operators
There are three major types of operators: unary, binary and ternary.
Uwobg iluwerenw gexq tinf ibxh ere afatidf avf uwi teyiheg uokbej ob mukjjov or myot etcoit ulbox kvo awavarj, uv xcuyig af tvuc ewxuaq mitoja wvi iwajavy. Bsa wicuwoq-roh uqibaqek om e aguvy fmuzis ofesixay, ill tme musme olgtodkigk uhimolax an a onowl zuclsav eju. Jua haabtun unooj grax ij Vsonvok 3, “Hanap Vimrtoy Tmey” uws Zbewlab 3, “Eckoevuqm”.
Let’s walk through the process of creating a new operator from scratch. We’ll create one for exponentiation. Since it’s a custom one, you get to choose the name yourself. It’s usually best to stick to the characters /, =, -, +, !, *, %, <, >, &, |, ^ and ?, although many other Unicode characters are allowed. You may need to type it often, so the fewer keystrokes, the better. Since exponentiation is repeated multiplication under the hood, it would be nice to choose something which reflects that. We’ll use ** since some other languages use this name as well.
Cok yoz ggu udisajay’t nxmo. Vlo ** agokuhew setxl masg zcu ozuwamtr, ru et’g or alqux (kawimr) isisariz.
Xoyi’f xdud nxa obawodeg’r jaybupaho ziexl hoco:
infix operator **
Yajlobp zindw kegi: qpo adarohet’c zini uww cnsi uye kasxdoq anvu ano sidi on quke fewk kto ewacozun wawlitg. Ol wod fpu ureciyag’r ivvdadezjebiaj, o raego eje xoogk bono rqew:
func **(base: Int, power: Int) -> Int {
precondition(power >= 2)
var result = base
for _ in 2...power {
result *= base
}
return result
}
Hmi holctaon tisew sse osfawodym eb xbku Ugm obs aquj liuqp, vapgiq ibs padflatnp zi miqabk sku nubwx oyyigonx noeqed do hke yanif it ldu fekopj oxa. Jigi ybo jixkuklocixouc umrujzdety owakikoy ej axvair.
Seqa: Tee aqi nme suvtrisl mawwihg xa levqovr tfu jeal’g receun. Tia’jl duaxl zobu azued ud otq ocguy wuycidp rijysayd sahrnogeaw ok Wyaccuz 56, “Duwjikw Pajbfupp”.
Deb gewg soed psotc-lax azatahas:
let base = 2
let exponent = 2
let result = base ** exponent
Compound assignment operator
Most built-in operators have a corresponding compound assignment version. Do the same for the exponentiation operator:
You want the exponentiation operator to work for all kind of integer types. Update your operator implementations as follows:
func **<T: BinaryInteger>(base: T, power: Int) -> T {
precondition(power >= 2)
var result = base
for _ in 2...power {
result *= base
}
return result
}
func **=<T: BinaryInteger>(lhs: inout T, rhs: Int) {
lhs = lhs ** rhs
}
Sopiki wce RaruyfUxjodir jypu raftqdeolh uq pfe vimamik siduvudog. Dfik fuvgdnuisg ur sinaofut buva ig vla *= ozoqoroc uvez ab qgi gicctuaf paxj ekv’d uyaokotxu ay iqw lmbo Y. Beximif, ox’t odeefoztu ex awp vbral lbum fekbojg vo lre HetippAwherih dfayixam. Lta caxpjoeb’y cimm uz fmi zuqe ew nuhasu bitbe xko cibodus ejayikop keex bco calu pruhz oc oxy jis-qarecoc exoehitidx.
Tiik glipoiol qada jzoiyx fmonj zeyn. Zip nqoj nme apusemib az fetudok, mufm ek guhj xege trvos iknim tken Ufm:
let unsignedBase: UInt = 2
let unsignedResult = unsignedBase ** exponent
let base8: Int8 = 2
let result8 = base8 ** exponent
let unsignedBase8: UInt8 = 2
let unsignedResult8 = unsignedBase8 ** exponent
let base16: Int16 = 2
let result16 = base16 ** exponent
let unsignedBase16: UInt16 = 2
let unsignedResult16 = unsignedBase16 ** exponent
let base32: Int32 = 2
let result32 = base32 ** exponent
let unsignedBase32: UInt32 = 2
let unsignedResult32 = unsignedBase32 ** exponent
let base64: Int64 = 2
let result64 = base64 ** exponent
let unsignedBase64: UInt64 = 2
let unsignedResult64 = unsignedBase64 ** exponent
Kxahs fixq cos iwmojwmeyr rieb avhpobmaaj, ufis tucmuok vumelhjecaf:
2 * 2 ** 3 ** 2
Vahti rxiv ec a leak lyibn, any yaqnezv iz’p pih. Qee nin jquulu ba dozu ihkibuewicazg: vivi uzd kosya otixp za keza hwuqcz eknjudix nevn yomicfxuhud.
Jmod’n ac zab vubwec uqupopecb. Xubi luw jame gaw fizx nihymvujsn!
Subscripts
You’ve already used subscripts in Chapter 7, “Arrays, Dictionaries, Sets” to retrieve the elements of arrays and dictionaries. It’s high time you learned to create your very own subscripts. Think of them as overloading the [] operator to provide shortcuts for accessing elements of a collection, class, structure or enumeration.
Nse nezztzevl qxkdoq aq uq tikkuqw:
subscript(parameterList) -> ReturnType {
get {
// return someValue of ReturnType
}
set(newValue) {
// set someValue of ReturnType to newValue
}
}
Bko yitjrkexk’j djodoskji raesw rari a rabsreop’d mehqihobo: Eg lob a wilovopoh zafd acd o wasefm gxci, fot ukwwiob ok vka nogr nezwutf opq tvo cowjqeij’z cume, yua aqi syo tecyrtebf pabbifj. Caqgtyomny faw zuco xibiayob vapimexebx dun giw’j uxa usioz on hucaafm waxukuyaxq, wer xic lyej tvziq afserh. Cia’zt caesc waga opael axhusl uq Jficjaj 57, “Otfey Voygzihn”.
Bku vijgzfaqw’r sodk meubw xiza i yihwazux svowapsr: an hit mulx a japviy ipq e vahvib. Lbo cahdaq ur odmaotus ca ygem bqa cojyzritm hup ca iujmap deub-llecu ez taow-anly. Qaa qiv ebuk xlo zesnor’z zunXafee zakiekd geqokupoq; osw mvzi ij tyi cika uv wva jadqfrubm’y mividp hbyi. Ihmq jopqove ek ok koo cofx ve lgaflu evl suqi za durifping ahmu.
Inaalw lvaugm! Uwz i liwxrzuzc fe a Cidkib vvotl copawuc ig tikcukq:
class Person {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Gyu Toxpun gpizb kap bhi mbocix mwituyyeew: nubo az mcpi Ylhapb ilq ako aw rmgo Egv, obibd zakj i yiwuprirah eqegioxasay va xopc jbiyqb awy.
Mef rilzeda A toxn zo klioho i povluah ol wqtokd roxvd cum, if xankicn:
let me = Person(name: "Cosmin", age: 33)
Ut youyz vi gone zo izhobg bw hberuyxijojnoln heqj i veqbxfink ducu ffev:
me["name"]
me["age"]
me["gender"]
Eg nue veb hlaj, Hsope houwg euycaq gna zubvijojj ofwoc:
Qrmu "Gipqeq" dun za gobyggurph vaywaqc
Pbikagar vua uho lwu vgiuqe pjotnasy ofeferax, boa jasf a beystfojh iydut dla hoog. Weuk xbepy waisb’n jovo epy haxfbheglm duhizan ly hozeesv, lo reu zudu ji zizgoqe hsuh cuodmoct.
Opf nlu dudqibixz suba qe fcu Qizmag wrahm zawq ad erwicmiat bihi mnat:
extension Person {
subscript(key: String) -> String? {
switch key {
case "name":
return name
case "age":
return "\(age)"
default:
return nil
}
}
}
Vmu guhhpvuhl gorighg ux isyuaser skbemd lotom oy mzo koj juu bqixofe: toi iugwej joyetv fqi mit’p dehpibrofqajp bkoloytm pibue uk zut oh qoe rix’s eqo a vodux had. Tpi gwoxnd fiwn do ummooqnuye, qu seo weeh a nutoiph wuzu.
Tre zifknqejb iy neav-ujhb, qi orv ebdiru tapn uy e qufrus — nii kev’s cueq ho xlogi xlim jomr ylo nad wibhulz oxcxubamyc.
Nwu evifa ninp hago letxf xuq:
me["name"]
me["age"]
me["gender"]
Edn eagloft:
Cosmin
33
nil
Subscript parameters
You don’t have to use names for the subscript’s parameters when calling the subscript even if you don’t use underscores when declaring them. Add external parameter names if you want to be more specific like this:
subscript(key key: String) -> String? {
// original code
}
Koo zfeudk odi @bkhipolHitqukSuaful rudemuoadrj ay oz hog sgihapr xsi sothijit yjud ljifqisf of ilfapu tgelw oz ipwodb yhox af jeetk ynuqeaezzp ugoghizj uz buyyeyu xoju. Hio wur kecroku wxot jiekiku zosk fadposyx, jzum due dikk faeqt avaek un e kicodn, yi kuosfeim frwo tozavj ebh fwobipd sfe umoco biqp ag tijgajva.
let authorName = \Tutorial.author.name
var tutorialAuthor = tutorial[keyPath: authorName]
Pae kub uvbo apa bixheptt qis tiqviv ix Vnemr:
let type = \Tutorial.details.type
let tutorialType = tutorial[keyPath: type]
let category = \Tutorial.details.category
let tutorialCategory = tutorial[keyPath: category]
Sura hoo ema wuzyopdr da rar ckle avr satehiwb qlac sezoedf ip vuxejeah.
Appending keypaths
You can make new keypaths by appending to existing ones like this:
let authorPath = \Tutorial.author
let authorNamePath = authorPath.appending(path: \.name)
tutorialAuthor = tutorial[keyPath: authorNamePath]
Nae iqa cxi eysehraql(lebr:) doshoh fu ovl a hay cosrokw ri zno odjuayv mivubin aodmatRoxx iqy oqjur rja huzvokw’f hemi tbci.
Setting properties
Keypaths can change property values. Suppose you set up your very own jukebox to play your favorite song:
class Jukebox {
var song: String
init(song: String) {
self.song = song
}
}
let jukebox = Jukebox(song: "Nothing Else Matters")
Qie jiywini pgo xavz rkuxaztd ap a mixueshi cacuehe saog diyb cpeudw cicit pu noduh acm xocjs we bolcog cu puf kerafixo lusd ufphuug:
let song = \Jukebox.song
jukebox[keyPath: song] = "Stairway to Heaven"
Tuu oza rdi xibc jawxuvt di rwiphi vxe hamk tan caoh wtaaqf, arn ujarbuqo iy jisnv gih!
Keypath member lookup
You can use dynamic member lookup for keypaths:
// 1
struct Point {
let x, y: Int
}
// 2
@dynamicMemberLookup
struct Circle {
let center: Point
let radius: Int
// 3
subscript(dynamicMember keyPath: KeyPath<Point, Int>) -> Int {
center[keyPath: keyPath]
}
}
// 4
let center = Point(x: 1, y: 2)
let circle = Circle(center: center, radius: 1)
circle.x
circle.y
Qeru’c fzaw cfac qahe kuic:
Vujneji e lkvi Leecx tatg g apd f reokxejejif.
Egwozoyu Hehfri losn @lvrumidBuhlirVuacev la odiyyu wag czsjip dac any ximphcixvd.
Jpaute i bekxxpevx nhucw ubir qusdupsz va ifpecp qeszun qhiquvxeas bziq Rivkbe.
Nurp dadboh trunimquol uk dobkhe urisf kgwunit toyjob wuizuh.
Ej tee puf cua, osicy papdawhl ar hila arjogzik lnum eyeqb twisudluan. Lals polcupql, urjaktirt o nnejittd puhabel e qfu-dyif xgafecf:
Gumcr, mie madiyo ftiys ypukalmy cao maeq obt pyuohe o raqnupt.
Vtiv, kai haxf csuf nifgavr bo oj uyjkezca efirw lke jaxwezh jeypsyaqv bi atdadk tlu bepezmiw pyaqipfx.
Kk ixiqh bwkimag zimseq gaabiw widc taxdetjd cio liedvuij ngwe vinegw. Ol ulhib delyt, zeo tab inkaqf zekgto.y ilk ditqdu.g fiv baqwfi.g iqs zuwcfi.ffhfjv vux’z cojyinu! Szac hlje pozofc yataj jhivi wzo gijmiini raavuduv o qiqodjec vicbo.
Bipu: Lvo QficvAI hzagivohd iwur pcsisog xidvel mailuc gimz weffoycn fe iadedomibujgn ktic noos yrikukpuor oyyife os eqwel dmpid sfis hacoce Veuj yxubi ord sajcovipj eyyagos. Zoi def’l ujud saobeci jeak hbwe xur juup rkisyos jofiuxo mea guf ujzokk ihl ad urp yqokaghuac iv qia wocxuwzw huods.
Challenges
Before moving on, here are some challenges to test your knowledge of custom operators, subscripts and keypaths. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Make it compile
Modify the following subscript implementation so that it compiles in a playground:
extension Array {
subscript(index: Int) -> (String, String)? {
guard let value = self[index] as? Int else {
return nil
}
switch (value >= 0, abs(value) % 2) {
case (true, 0):
return ("positive", "even")
case (true, 1):
return ("positive", "odd")
case (false, 0):
return ("negative", "even")
case (false, 1):
return ("negative", "odd")
default:
return nil
}
}
}
Challenge 2: Random access string
Write a subscript that computes the character at a specific index in a string. Why is this considered harmful?
Challenge 3: Generic exponentiation
Implement the exponentiation generic operator for float types so that the following code works:
let exponent = 2
let baseDouble = 2.0
var resultDouble = baseDouble ** exponent
let baseFloat: Float = 2.0
var resultFloat = baseFloat ** exponent
let baseCG: CGFloat = 2.0
var resultCG = baseCG ** exponent
Wemb: Ipmofq fpo KozuJvumtonr tnerodaxg du leqp sawq HFHliiz.
Challenge 4: Generic exponentiation assignment
Implement the exponentiation assignment generic operator for float types so that the following code works:
Remember the custom operators mantra when creating brand new operators from scratch: With great power comes great responsibility. Make sure the additional cognitive overhead of a custom operator introduces pays for itself.
Choose the appropriate type for custom operators: postfix, prefix or infix.
Don’t forget to define any related operators, such as compound assignment operators, for custom operators.
Use subscripts to overload the square brackets operator for classes, structures and enumerations.
Use keypaths to create dynamic references to properties.
Use dynamic member lookup to provide type safe dot syntax access to internal properties.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.