[{"data":1,"prerenderedAt":1420},["ShallowReactive",2],{"\u002Fblogs\u002Fa-deep-dive-into-go-compile-time-interface-assertions":3},{"id":4,"title":5,"body":6,"coverImage":1405,"description":1407,"extension":1408,"meta":1409,"navigation":142,"path":1411,"publishedOn":1412,"seo":1413,"sitemap":1414,"stem":1418,"__hash__":1419},"content\u002Fblogs\u002Fa-deep-dive-into-go-compile-time-interface-assertions.md","A Deep Dive Into Go's Compile-Time Interface Assertions",{"type":7,"value":8,"toc":1389},"minimark",[9,13,16,71,74,88,91,94,99,102,105,170,173,182,302,310,318,321,324,363,366,379,382,385,388,392,395,425,435,438,446,449,459,466,468,505,511,521,527,530,551,554,563,566,596,602,608,613,630,636,639,649,652,685,688,694,697,703,721,727,730,760,763,774,777,781,784,814,817,822,825,828,843,846,851,863,867,870,875,878,910,919,922,928,931,1086,1103,1106,1159,1181,1203,1206,1209,1236,1239,1248,1251,1254,1258,1261,1264,1349,1355,1358,1365,1369,1379,1382,1385],[10,11,12],"p",{},"Go is a language that favours simplicity, but many of its design decisions can\ninitially feel unusual. Especially if you are coming languages with explicit\ninheritance models or dynamically typed languages such as Python, you are bound\nto feel confused with the language.",[10,14,15],{},"One particular idiom caught me off guard when I first encountered it:",[17,18,23],"pre",{"className":19,"code":20,"language":21,"meta":22,"style":22},"language-go shiki shiki-themes everforest-dark","var _ io.Reader = (*File)(nil)\n","go","",[24,25,26],"code",{"__ignoreMap":22},[27,28,31,35,39,43,46,49,52,55,58,61,64,68],"span",{"class":29,"line":30},"line",1,[27,32,34],{"class":33},"sDOmQ","var",[27,36,38],{"class":37},"sY0Nt"," _ ",[27,40,42],{"class":41},"sP-ue","io",[27,44,45],{"class":37},".",[27,47,48],{"class":41},"Reader",[27,50,51],{"class":33}," =",[27,53,54],{"class":37}," (",[27,56,57],{"class":33},"*",[27,59,60],{"class":41},"File",[27,62,63],{"class":37},")(",[27,65,67],{"class":66},"s9lfW","nil",[27,69,70],{"class":37},")\n",[10,72,73],{},"If you've spent any amount of time reading Go code, there's a good chance you've\ncome across this line, or some variation of it. It's especially common in\nlibraries, generated code, and large codebases.",[10,75,76,77,80,81,83,84,87],{},"At first glance, the syntax is puzzling. Why are we assigning to ",[24,78,79],{},"_","? Why is a\n",[24,82,67],{}," pointer involved? Is this creating a variable? Is ",[24,85,86],{},"(*File)"," a type\nconversion or a dereference operation?",[10,89,90],{},"As it turns out, this seemingly cryptic line is a deliberate compile-time safety\nmechanism used as a convention among Go developers to verify if a type satisfies\nan interface.",[10,92,93],{},"In this article, we'll break down exactly how this pattern works, why it exists,\nand how understanding it provides deeper insight into Go's type system and its\nphilosophy of implicit interfaces.",[95,96,98],"h2",{"id":97},"a-quick-refresher-on-go-interfaces","A Quick Refresher on Go Interfaces",[10,100,101],{},"Unlike many object-oriented languages, Go does not require types to explicitly\ndeclare an implementation of an interface.",[10,103,104],{},"For example, in TypeScript you might write:",[17,106,110],{"className":107,"code":108,"language":109,"meta":22,"style":22},"language-typescript shiki shiki-themes everforest-dark","interface Reader {\n    ...\n}\n\nclass File implements Reader {\n    ...\n}\n","typescript",[24,111,112,125,131,137,144,160,165],{"__ignoreMap":22},[27,113,114,118,122],{"class":29,"line":30},[27,115,117],{"class":116},"safYi","interface",[27,119,121],{"class":120},"sFWo_"," Reader",[27,123,124],{"class":37}," {\n",[27,126,128],{"class":29,"line":127},2,[27,129,130],{"class":33},"    ...\n",[27,132,134],{"class":29,"line":133},3,[27,135,136],{"class":37},"}\n",[27,138,140],{"class":29,"line":139},4,[27,141,143],{"emptyLinePlaceholder":142},true,"\n",[27,145,147,150,153,156,158],{"class":29,"line":146},5,[27,148,149],{"class":116},"class",[27,151,152],{"class":120}," File",[27,154,155],{"class":33}," implements",[27,157,121],{"class":120},[27,159,124],{"class":37},[27,161,163],{"class":29,"line":162},6,[27,164,130],{"class":33},[27,166,168],{"class":29,"line":167},7,[27,169,136],{"class":37},[10,171,172],{},"Go takes a different approach.",[10,174,175,176,178,179,181],{},"Suppose we define a ",[24,177,48],{}," interface and a ",[24,180,60],{}," type:",[17,183,185],{"className":19,"code":184,"language":21,"meta":22,"style":22},"type Reader interface {\n    Read([]byte) (int, error)\n}\n\ntype File struct{}\n\nfunc (f *File) Read(b []byte) (int, error) {\n    return 0, nil\n}\n",[24,186,187,199,225,229,233,245,249,283,297],{"__ignoreMap":22},[27,188,189,192,194,197],{"class":29,"line":30},[27,190,191],{"class":116},"type",[27,193,121],{"class":41},[27,195,196],{"class":116}," interface",[27,198,124],{"class":37},[27,200,201,205,208,211,214,217,220,223],{"class":29,"line":127},[27,202,204],{"class":203},"sRC7j","    Read",[27,206,207],{"class":37},"([]",[27,209,210],{"class":41},"byte",[27,212,213],{"class":37},") (",[27,215,216],{"class":41},"int",[27,218,219],{"class":37},", ",[27,221,222],{"class":41},"error",[27,224,70],{"class":37},[27,226,227],{"class":29,"line":133},[27,228,136],{"class":37},[27,230,231],{"class":29,"line":139},[27,232,143],{"emptyLinePlaceholder":142},[27,234,235,237,239,242],{"class":29,"line":146},[27,236,191],{"class":116},[27,238,152],{"class":41},[27,240,241],{"class":116}," struct",[27,243,244],{"class":37},"{}\n",[27,246,247],{"class":29,"line":162},[27,248,143],{"emptyLinePlaceholder":142},[27,250,251,254,257,259,261,264,267,270,272,274,276,278,280],{"class":29,"line":167},[27,252,253],{"class":116},"func",[27,255,256],{"class":37}," (f ",[27,258,57],{"class":33},[27,260,60],{"class":41},[27,262,263],{"class":37},") ",[27,265,266],{"class":203},"Read",[27,268,269],{"class":37},"(b []",[27,271,210],{"class":41},[27,273,213],{"class":37},[27,275,216],{"class":41},[27,277,219],{"class":37},[27,279,222],{"class":41},[27,281,282],{"class":37},") {\n",[27,284,286,289,292,294],{"class":29,"line":285},8,[27,287,288],{"class":116},"    return",[27,290,291],{"class":66}," 0",[27,293,219],{"class":37},[27,295,296],{"class":66},"nil\n",[27,298,300],{"class":29,"line":299},9,[27,301,136],{"class":37},[10,303,304,305,307,308,45],{},"Then, at no point do we explicitly tell Go to implement ",[24,306,60],{}," as a concrete\ntype of ",[24,309,48],{},[10,311,312,313,317],{},"Instead, Go is designed to use what is known as ",[314,315,316],"strong",{},"structural typing",". If a\ntype's method set contains all the methods required by an interface, the type\nautomatically satisfies that interface.",[10,319,320],{},"The compiler performs this check whenever the type is used in a context which\nexpects that interface.",[10,322,323],{},"For example:",[17,325,327],{"className":19,"code":326,"language":21,"meta":22,"style":22},"var r Reader\n\nr = &File{} \u002F\u002F Valid\n",[24,328,329,339,343],{"__ignoreMap":22},[27,330,331,333,336],{"class":29,"line":30},[27,332,34],{"class":33},[27,334,335],{"class":37}," r ",[27,337,338],{"class":41},"Reader\n",[27,340,341],{"class":29,"line":127},[27,342,143],{"emptyLinePlaceholder":142},[27,344,345,348,351,354,356,359],{"class":29,"line":133},[27,346,347],{"class":37},"r ",[27,349,350],{"class":33},"=",[27,352,353],{"class":33}," &",[27,355,60],{"class":41},[27,357,358],{"class":37},"{} ",[27,360,362],{"class":361},"s67c6","\u002F\u002F Valid\n",[10,364,365],{},"In the example above, if you compiled the code, the compiler asks a simple\nquestion:",[367,368,369],"blockquote",{},[10,370,371,372,375,376,378],{},"Does ",[24,373,374],{},"*File"," implement every method required by ",[24,377,48],{},"?",[10,380,381],{},"If the answer is yes and the assignment is allowed. If not, compilation fails.",[10,383,384],{},"This implicit implementation model is one of Go's defining characteristics. It\nkeeps interfaces decoupled from their implementations and allows interfaces to\nbe defined by the code consuming them rather than the code providing them.",[10,386,387],{},"Understanding this behaviour is important because the mysterious line we're\ninvestigating later in this article is simply another way of forcing the\ncompiler to perform this exact same interface check.",[95,389,391],{"id":390},"breaking-down-compile-time-interface-assertions","Breaking Down Compile-Time Interface Assertions",[10,393,394],{},"Now that we understand how Go interfaces work, let's revisit the line that\nbrought us here in the first place:",[17,396,397],{"className":19,"code":20,"language":21,"meta":22,"style":22},[24,398,399],{"__ignoreMap":22},[27,400,401,403,405,407,409,411,413,415,417,419,421,423],{"class":29,"line":30},[27,402,34],{"class":33},[27,404,38],{"class":37},[27,406,42],{"class":41},[27,408,45],{"class":37},[27,410,48],{"class":41},[27,412,51],{"class":33},[27,414,54],{"class":37},[27,416,57],{"class":33},[27,418,60],{"class":41},[27,420,63],{"class":37},[27,422,67],{"class":66},[27,424,70],{"class":37},[10,426,427,428,430,431,434],{},"At first glance, this syntax appears cryptic. However, it is simply leveraging\nexisting Go language features in a clever way to force the compiler to verify if\n",[24,429,374],{}," satisfies the ",[24,432,433],{},"io.Reader"," interface.",[10,436,437],{},"Let's break it down piece by piece.",[439,440,442,443],"h3",{"id":441},"the-right-hand-side-filenil","The Right-Hand Side: ",[24,444,445],{},"(*File)(nil)",[10,447,448],{},"This is often the most confusing part.",[10,450,451,452,454,455,458],{},"Contrary to what many developers initially assume, ",[24,453,86],{}," is ",[314,456,457],{},"not\ndereferencing"," anything. Instead, this is a type conversion expression.",[10,460,461,462,465],{},"The syntax: ",[24,463,464],{},"(Type)(value)"," is used throughout Go to convert values from one\ntype to another.",[10,467,323],{},[17,469,471],{"className":19,"code":470,"language":21,"meta":22,"style":22},"type MyInt int\n\nn := MyInt(42)\n",[24,472,473,483,487],{"__ignoreMap":22},[27,474,475,477,480],{"class":29,"line":30},[27,476,191],{"class":116},[27,478,479],{"class":41}," MyInt",[27,481,482],{"class":41}," int\n",[27,484,485],{"class":29,"line":127},[27,486,143],{"emptyLinePlaceholder":142},[27,488,489,492,495,497,500,503],{"class":29,"line":133},[27,490,491],{"class":37},"n ",[27,493,494],{"class":33},":=",[27,496,479],{"class":203},[27,498,499],{"class":37},"(",[27,501,502],{"class":66},"42",[27,504,70],{"class":37},[10,506,507,508,510],{},"The same concept applies for ",[24,509,445],{}," which simply means:",[367,512,513],{},[10,514,515,516,518,519,45],{},"Convert ",[24,517,67],{}," into a value of type ",[24,520,374],{},[10,522,523,524,526],{},"This does not allocate any memory and does not create a ",[24,525,60],{}," instance.",[10,528,529],{},"It is conceptually equivalent to:",[17,531,533],{"className":19,"code":532,"language":21,"meta":22,"style":22},"var f *File = nil\n",[24,534,535],{"__ignoreMap":22},[27,536,537,539,542,544,546,548],{"class":29,"line":30},[27,538,34],{"class":33},[27,540,541],{"class":37}," f ",[27,543,57],{"class":33},[27,545,60],{"class":41},[27,547,51],{"class":33},[27,549,550],{"class":66}," nil\n",[10,552,553],{},"The actual value is irrelevant. We only care about the type.",[10,555,556,557,559,560,45],{},"You may also be wondering why the syntax is written as ",[24,558,445],{}," instead of\nsimply ",[24,561,562],{},"*File(nil)",[10,564,565],{},"The answer lies in Go's parsing rules for type conversions.",[10,567,568,569,572,573,576,577,580,581,583,584,586,587,589,590,592,593,595],{},"In Go, type conversions follow the ",[24,570,571],{},"Type(value)"," syntax. This works without\nadditional parentheses for simple type names such as ",[24,574,575],{},"int(42)"," or\n",[24,578,579],{},"MyType(value)",". However, ",[24,582,374],{}," is not a simple type name but a pointer type\nexpression. Wrapping it in parentheses tells the compiler to treat the entire\n",[24,585,374],{}," expression as the target type of the conversion. Without the\nparentheses, ",[24,588,562],{}," would be parsed differently and would no longer\nrepresent \"convert ",[24,591,67],{}," to a ",[24,594,374],{},"\".",[10,597,598,599,601],{},"In other words, ",[24,600,445],{}," is not special syntax invented for compile-time\ninterface assertions; it is simply the standard Go type conversion syntax\napplied to a pointer type.",[439,603,605,606],{"id":604},"the-interface-type-ioreader","The Interface Type: ",[24,607,433],{},[10,609,610,611,45],{},"The left-hand side declares a variable of type ",[24,612,433],{},[17,614,616],{"className":19,"code":615,"language":21,"meta":22,"style":22},"var _ io.Reader\n",[24,617,618],{"__ignoreMap":22},[27,619,620,622,624,626,628],{"class":29,"line":30},[27,621,34],{"class":33},[27,623,38],{"class":37},[27,625,42],{"class":41},[27,627,45],{"class":37},[27,629,338],{"class":41},[10,631,632,633,635],{},"By itself, this is unremarkable. However, assigning ",[24,634,445],{}," to it\ntriggers Go's normal interface compatibility checks.",[10,637,638],{},"The compiler now asks:",[367,640,641],{},[10,642,643,644,646,647,378],{},"Can a value of type ",[24,645,374],{}," be assigned to a variable of type ",[24,648,433],{},[10,650,651],{},"This is the exact same check that would occur here:",[17,653,655],{"className":19,"code":654,"language":21,"meta":22,"style":22},"var r io.Reader\n\nr = &File{}\n",[24,656,657,669,673],{"__ignoreMap":22},[27,658,659,661,663,665,667],{"class":29,"line":30},[27,660,34],{"class":33},[27,662,335],{"class":37},[27,664,42],{"class":41},[27,666,45],{"class":37},[27,668,338],{"class":41},[27,670,671],{"class":29,"line":127},[27,672,143],{"emptyLinePlaceholder":142},[27,674,675,677,679,681,683],{"class":29,"line":133},[27,676,347],{"class":37},[27,678,350],{"class":33},[27,680,353],{"class":33},[27,682,60],{"class":41},[27,684,244],{"class":37},[10,686,687],{},"The difference is in our intention to force the compiler to check the\nimplementtation without needing to create a real variable to be used later.",[439,689,691,692],{"id":690},"the-blank-identifier-_","The Blank Identifier: ",[24,693,79],{},[10,695,696],{},"The final piece of the puzzle is the blank identifier.",[10,698,699,700,702],{},"In Go, ",[24,701,79],{}," discards values, so in the following statement;",[17,704,706],{"className":19,"code":705,"language":21,"meta":22,"style":22},"result, _ := someFunction()\n",[24,707,708],{"__ignoreMap":22},[27,709,710,713,715,718],{"class":29,"line":30},[27,711,712],{"class":37},"result, _ ",[27,714,494],{"class":33},[27,716,717],{"class":203}," someFunction",[27,719,720],{"class":37},"()\n",[10,722,723,724,726],{},"Anything assigned to ",[24,725,79],{}," is immediately ignored and garbage-collected by the\ncompiler.",[10,728,729],{},"By writing:",[17,731,732],{"className":19,"code":20,"language":21,"meta":22,"style":22},[24,733,734],{"__ignoreMap":22},[27,735,736,738,740,742,744,746,748,750,752,754,756,758],{"class":29,"line":30},[27,737,34],{"class":33},[27,739,38],{"class":37},[27,741,42],{"class":41},[27,743,45],{"class":37},[27,745,48],{"class":41},[27,747,51],{"class":33},[27,749,54],{"class":37},[27,751,57],{"class":33},[27,753,60],{"class":41},[27,755,63],{"class":37},[27,757,67],{"class":66},[27,759,70],{"class":37},[10,761,762],{},"we are effectively telling the compiler:",[367,764,765],{},[10,766,767,768,770,771,773],{},"Verify that ",[24,769,374],{}," implements ",[24,772,433],{},", then throw everything away.",[10,775,776],{},"No variable is created for later use and no runtime behaviour is introduced. The\nstatement exists solely to perform a compile-time check.",[439,778,780],{"id":779},"putting-it-all-together","Putting It All Together",[10,782,783],{},"When the compiler encounters:",[17,785,786],{"className":19,"code":20,"language":21,"meta":22,"style":22},[24,787,788],{"__ignoreMap":22},[27,789,790,792,794,796,798,800,802,804,806,808,810,812],{"class":29,"line":30},[27,791,34],{"class":33},[27,793,38],{"class":37},[27,795,42],{"class":41},[27,797,45],{"class":37},[27,799,48],{"class":41},[27,801,51],{"class":33},[27,803,54],{"class":37},[27,805,57],{"class":33},[27,807,60],{"class":41},[27,809,63],{"class":37},[27,811,67],{"class":66},[27,813,70],{"class":37},[10,815,816],{},"it internally reasons about it as:",[367,818,819],{},[10,820,821],{},"Can a value of type *File be assigned to a variable of type io.Reader?",[10,823,824],{},"If the answer is yes, compilation succeeds. If the answer is no, compilation\nfails immediately.",[10,826,827],{},"As an example, the following concrete type which does not have the full method\nset of an interface like this:",[17,829,831],{"className":19,"code":830,"language":21,"meta":22,"style":22},"type File struct{}\n",[24,832,833],{"__ignoreMap":22},[27,834,835,837,839,841],{"class":29,"line":30},[27,836,191],{"class":116},[27,838,152],{"class":41},[27,840,241],{"class":116},[27,842,244],{"class":37},[10,844,845],{},"would produce an error similar to:",[367,847,848],{},[10,849,850],{},"*File does not implement io.Reader (missing method Read)",[10,852,853,854,857,858,862],{},"This pattern does not introduce any special language behaviour. The Go lexicon\nhas no ",[24,855,856],{},"implements"," keyword or dedicated compiler feature involved to check for\ncompile-time interface assertions. Instead, compile-time interface assertions\nare simply a clever application of Go's existing assignment and interface\nsatisfaction rules developed and ",[859,860,861],"em",{},"de facto"," standardised by the community.",[95,864,866],{"id":865},"why-do-we-need-this-pattern","Why Do We Need This Pattern?",[10,868,869],{},"At this point, a natural question arises:",[367,871,872],{},[10,873,874],{},"If Go already checks whether a type satisfies an interface, why do we need to\nexplicitly write compile-time interface assertions at all?",[10,876,877],{},"After all, the compiler already performs this check whenever a type is assigned\nto an interface.",[17,879,880],{"className":19,"code":654,"language":21,"meta":22,"style":22},[24,881,882,894,898],{"__ignoreMap":22},[27,883,884,886,888,890,892],{"class":29,"line":30},[27,885,34],{"class":33},[27,887,335],{"class":37},[27,889,42],{"class":41},[27,891,45],{"class":37},[27,893,338],{"class":41},[27,895,896],{"class":29,"line":127},[27,897,143],{"emptyLinePlaceholder":142},[27,899,900,902,904,906,908],{"class":29,"line":133},[27,901,347],{"class":37},[27,903,350],{"class":33},[27,905,353],{"class":33},[27,907,60],{"class":41},[27,909,244],{"class":37},[10,911,912,913,915,916,918],{},"If ",[24,914,374],{}," does not implement ",[24,917,433],{},", compilation will fail anyway.",[10,920,921],{},"So why introduce another line of code?",[10,923,924,925,45],{},"The answer is that, the compiler only performs interface satisfaction checks\n",[314,926,927],{},"when a type is used in a context expecting that interface",[10,929,930],{},"Consider the following example:",[17,932,934],{"className":19,"code":933,"language":21,"meta":22,"style":22},"type Storage interface {\n    Save([]byte) error\n    Load() ([]byte, error)\n}\n\ntype FileStorage struct{}\n\nfunc (f *FileStorage) Save(data []byte) error {\n    return nil\n}\n\nfunc (f *FileStorage) Load() ([]byte, error) {\n    return nil, nil\n}\n",[24,935,936,947,961,977,981,985,996,1000,1027,1033,1038,1043,1069,1081],{"__ignoreMap":22},[27,937,938,940,943,945],{"class":29,"line":30},[27,939,191],{"class":116},[27,941,942],{"class":41}," Storage",[27,944,196],{"class":116},[27,946,124],{"class":37},[27,948,949,952,954,956,958],{"class":29,"line":127},[27,950,951],{"class":203},"    Save",[27,953,207],{"class":37},[27,955,210],{"class":41},[27,957,263],{"class":37},[27,959,960],{"class":41},"error\n",[27,962,963,966,969,971,973,975],{"class":29,"line":133},[27,964,965],{"class":203},"    Load",[27,967,968],{"class":37},"() ([]",[27,970,210],{"class":41},[27,972,219],{"class":37},[27,974,222],{"class":41},[27,976,70],{"class":37},[27,978,979],{"class":29,"line":139},[27,980,136],{"class":37},[27,982,983],{"class":29,"line":146},[27,984,143],{"emptyLinePlaceholder":142},[27,986,987,989,992,994],{"class":29,"line":162},[27,988,191],{"class":116},[27,990,991],{"class":41}," FileStorage",[27,993,241],{"class":116},[27,995,244],{"class":37},[27,997,998],{"class":29,"line":167},[27,999,143],{"emptyLinePlaceholder":142},[27,1001,1002,1004,1006,1008,1011,1013,1016,1019,1021,1023,1025],{"class":29,"line":285},[27,1003,253],{"class":116},[27,1005,256],{"class":37},[27,1007,57],{"class":33},[27,1009,1010],{"class":41},"FileStorage",[27,1012,263],{"class":37},[27,1014,1015],{"class":203},"Save",[27,1017,1018],{"class":37},"(data []",[27,1020,210],{"class":41},[27,1022,263],{"class":37},[27,1024,222],{"class":41},[27,1026,124],{"class":37},[27,1028,1029,1031],{"class":29,"line":299},[27,1030,288],{"class":116},[27,1032,550],{"class":66},[27,1034,1036],{"class":29,"line":1035},10,[27,1037,136],{"class":37},[27,1039,1041],{"class":29,"line":1040},11,[27,1042,143],{"emptyLinePlaceholder":142},[27,1044,1046,1048,1050,1052,1054,1056,1059,1061,1063,1065,1067],{"class":29,"line":1045},12,[27,1047,253],{"class":116},[27,1049,256],{"class":37},[27,1051,57],{"class":33},[27,1053,1010],{"class":41},[27,1055,263],{"class":37},[27,1057,1058],{"class":203},"Load",[27,1060,968],{"class":37},[27,1062,210],{"class":41},[27,1064,219],{"class":37},[27,1066,222],{"class":41},[27,1068,282],{"class":37},[27,1070,1072,1074,1077,1079],{"class":29,"line":1071},13,[27,1073,288],{"class":116},[27,1075,1076],{"class":66}," nil",[27,1078,219],{"class":37},[27,1080,296],{"class":66},[27,1082,1084],{"class":29,"line":1083},14,[27,1085,136],{"class":37},[10,1087,1088,1089,1092,1093,1096,1097,1099,1100,1102],{},"At this point, ",[24,1090,1091],{},"*FileStorage"," implicitly satisfies the ",[24,1094,1095],{},"Storage"," interface.\nHowever, nowhere in the package are we explicitly assigning ",[24,1098,1091],{}," to a\nvariable of type ",[24,1101,1095],{},". This means the compiler has no reason to verify the\nrelationship yet.",[10,1104,1105],{},"Now imagine a future refactor.",[17,1107,1109],{"className":19,"code":1108,"language":21,"meta":22,"style":22},"func (f *FileStorage) Load() ([]byte, error, bool) {\n    return nil, nil, false\n}\n",[24,1110,1111,1140,1155],{"__ignoreMap":22},[27,1112,1113,1115,1117,1119,1121,1123,1125,1127,1129,1131,1133,1135,1138],{"class":29,"line":30},[27,1114,253],{"class":116},[27,1116,256],{"class":37},[27,1118,57],{"class":33},[27,1120,1010],{"class":41},[27,1122,263],{"class":37},[27,1124,1058],{"class":203},[27,1126,968],{"class":37},[27,1128,210],{"class":41},[27,1130,219],{"class":37},[27,1132,222],{"class":41},[27,1134,219],{"class":37},[27,1136,1137],{"class":41},"bool",[27,1139,282],{"class":37},[27,1141,1142,1144,1146,1148,1150,1152],{"class":29,"line":127},[27,1143,288],{"class":116},[27,1145,1076],{"class":66},[27,1147,219],{"class":37},[27,1149,67],{"class":66},[27,1151,219],{"class":37},[27,1153,1154],{"class":66},"false\n",[27,1156,1157],{"class":29,"line":133},[27,1158,136],{"class":37},[10,1160,1161,1162,1164,1165,1167,1168,1170,1171,1174,1175,1177,1178,1180],{},"Now ",[24,1163,1010],{}," no longer satisfies ",[24,1166,1095],{}," but without a compile-time\ninterface assertion, this may not be discovered until another package attempts\nto use it as a ",[24,1169,1095],{}," . For example, here’s a statement which defines a\nvariable ",[24,1172,1173],{},"s"," of type ",[24,1176,1095],{}," which stores an instance of the ",[24,1179,1010],{},"\ntype.",[17,1182,1184],{"className":19,"code":1183,"language":21,"meta":22,"style":22},"var s Storage = &FileStorage{}\n",[24,1185,1186],{"__ignoreMap":22},[27,1187,1188,1190,1193,1195,1197,1199,1201],{"class":29,"line":30},[27,1189,34],{"class":33},[27,1191,1192],{"class":37}," s ",[27,1194,1095],{"class":41},[27,1196,51],{"class":33},[27,1198,353],{"class":33},[27,1200,1010],{"class":41},[27,1202,244],{"class":37},[10,1204,1205],{},"Depending on the size of the codebase, this usage may be located far away from\nthe implementation itself which is often the case in a medium to large-scale\ncodebase.",[10,1207,1208],{},"Compile-time interface assertions move that verification closer to where it\nmatters.",[17,1210,1212],{"className":19,"code":1211,"language":21,"meta":22,"style":22},"var _ Storage = (*FileStorage)(nil)\n",[24,1213,1214],{"__ignoreMap":22},[27,1215,1216,1218,1220,1222,1224,1226,1228,1230,1232,1234],{"class":29,"line":30},[27,1217,34],{"class":33},[27,1219,38],{"class":37},[27,1221,1095],{"class":41},[27,1223,51],{"class":33},[27,1225,54],{"class":37},[27,1227,57],{"class":33},[27,1229,1010],{"class":41},[27,1231,63],{"class":37},[27,1233,67],{"class":66},[27,1235,70],{"class":37},[10,1237,1238],{},"Now the package itself declares an expectation:",[367,1240,1241],{},[10,1242,1243,1245,1246,45],{},[24,1244,1091],{}," is intended to satisfy ",[24,1247,1095],{},[10,1249,1250],{},"If the contract is accidentally ever broken during a refactor, compilation fails\ngiving the developer ample headroom to decide the next course of action without\nintroducing breaking changes.",[10,1252,1253],{},"This is especially valuable in larger codebases where interfaces and their\nimplementations often live in separate packages.",[439,1255,1257],{"id":1256},"it-also-serves-as-documentation","It Also Serves as Documentation",[10,1259,1260],{},"Compile-time interface assertions are not only a safety mechanism, they are also\na form of executable documentation.",[10,1262,1263],{},"When another developer encounters:",[17,1265,1267],{"className":19,"code":1266,"language":21,"meta":22,"style":22},"var _ io.Reader = (*File)(nil)\nvar _ io.Writer = (*File)(nil)\nvar _ io.Closer = (*File)(nil)\n",[24,1268,1269,1295,1322],{"__ignoreMap":22},[27,1270,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293],{"class":29,"line":30},[27,1272,34],{"class":33},[27,1274,38],{"class":37},[27,1276,42],{"class":41},[27,1278,45],{"class":37},[27,1280,48],{"class":41},[27,1282,51],{"class":33},[27,1284,54],{"class":37},[27,1286,57],{"class":33},[27,1288,60],{"class":41},[27,1290,63],{"class":37},[27,1292,67],{"class":66},[27,1294,70],{"class":37},[27,1296,1297,1299,1301,1303,1305,1308,1310,1312,1314,1316,1318,1320],{"class":29,"line":127},[27,1298,34],{"class":33},[27,1300,38],{"class":37},[27,1302,42],{"class":41},[27,1304,45],{"class":37},[27,1306,1307],{"class":41},"Writer",[27,1309,51],{"class":33},[27,1311,54],{"class":37},[27,1313,57],{"class":33},[27,1315,60],{"class":41},[27,1317,63],{"class":37},[27,1319,67],{"class":66},[27,1321,70],{"class":37},[27,1323,1324,1326,1328,1330,1332,1335,1337,1339,1341,1343,1345,1347],{"class":29,"line":133},[27,1325,34],{"class":33},[27,1327,38],{"class":37},[27,1329,42],{"class":41},[27,1331,45],{"class":37},[27,1333,1334],{"class":41},"Closer",[27,1336,51],{"class":33},[27,1338,54],{"class":37},[27,1340,57],{"class":33},[27,1342,60],{"class":41},[27,1344,63],{"class":37},[27,1346,67],{"class":66},[27,1348,70],{"class":37},[10,1350,1351,1352,1354],{},"they can immediately infer the intended capabilities of the type (",[24,1353,374],{}," in the\ncurrent context) without searching through the entire codebase.",[10,1356,1357],{},"It clearly communicates:",[367,1359,1360],{},[10,1361,1362,1364],{},[24,1363,60],{}," is expected to behave as a reader, writer, and closer.",[439,1366,1368],{"id":1367},"this-is-why-generated-code-uses-it-so-frequently","This Is Why Generated Code Uses It So Frequently",[10,1370,1371,1372,219,1375,1378],{},"Tools such as ",[24,1373,1374],{},"oapi-codegen",[24,1376,1377],{},"sqlc",", and various mocking or RPC generators\noften emit compile-time interface assertions.",[10,1380,1381],{},"Generated code typically establishes contracts between generated interfaces and\ndeveloper-provided implementations. By adding these assertions, generated code\ncan immediately detect when an implementation has drifted away from the expected\ncontract. Instead of discovering the issue later, developers receive a\ncompile-time error as soon as the interface relationship is broken.",[10,1383,1384],{},"Ultimately, compile-time interface assertions are not introducing a new\nfunctionality to the language. They are a deliberate way of making an implicit\nrelationship explicit, and ensuring that relationship remains intact over time.",[1386,1387,1388],"style",{},"html pre.shiki code .sDOmQ, html code.shiki .sDOmQ{--shiki-default:#E69875}html pre.shiki code .sY0Nt, html code.shiki .sY0Nt{--shiki-default:#D3C6AA}html pre.shiki code .sP-ue, html code.shiki .sP-ue{--shiki-default:#7FBBB3}html pre.shiki code .s9lfW, html code.shiki .s9lfW{--shiki-default:#D699B6}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .safYi, html code.shiki .safYi{--shiki-default:#E67E80}html pre.shiki code .sFWo_, html code.shiki .sFWo_{--shiki-default:#83C092}html pre.shiki code .sRC7j, html code.shiki .sRC7j{--shiki-default:#A7C080}html pre.shiki code .s67c6, html code.shiki .s67c6{--shiki-default:#859289;--shiki-default-font-style:italic}",{"title":22,"searchDepth":127,"depth":127,"links":1390},[1391,1392,1401],{"id":97,"depth":127,"text":98},{"id":390,"depth":127,"text":391,"children":1393},[1394,1396,1398,1400],{"id":441,"depth":133,"text":1395},"The Right-Hand Side: (*File)(nil)",{"id":604,"depth":133,"text":1397},"The Interface Type: io.Reader",{"id":690,"depth":133,"text":1399},"The Blank Identifier: _",{"id":779,"depth":133,"text":780},{"id":865,"depth":127,"text":866,"children":1402},[1403,1404],{"id":1256,"depth":133,"text":1257},{"id":1367,"depth":133,"text":1368},{"url":1406,"alt":5},"https:\u002F\u002Fik.imagekit.io\u002Fjarmos\u002Fa-deep-dive-into-go-compile-time-interface-assertions.png","Learn how pointers work in Go with clear explanations and practical examples. This guide covers pointer types, memory addresses, dereferencing and why pointers matter for writing efficient and reliable Go programs.","md",{"status":1410},"published","\u002Fblogs\u002Fa-deep-dive-into-go-compile-time-interface-assertions","2026-06-18",{"title":5,"description":1407},{"loc":1415,"lastmod":1416,"changefreq":1417,"priority":30},"\u002Fa-deep-dive-into-go-compile-time-interface-assertions","2026-06-18 12:06:41","yearly","blogs\u002Fa-deep-dive-into-go-compile-time-interface-assertions","YAzOpjtli-BwLwfdzx01ln8XE065ou_59u1WJ5OeX6U",1788263772940]