{"id":444,"date":"2020-06-15T00:00:00","date_gmt":"2020-06-15T00:00:00","guid":{"rendered":"https:\/\/en.pingcap.com\/blog\/generics-and-compile-time-in-rust\/"},"modified":"2024-06-03T06:14:20","modified_gmt":"2024-06-03T13:14:20","slug":"generics-and-compile-time-in-rust","status":"publish","type":"post","link":"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/","title":{"rendered":"Generics and Compile-Time in Rust"},"content":{"rendered":"<p>The Rust programming language compiles fast software slowly.<\/p>\n<p>In this series we explore Rust&#8217;s compile times within the context of TiKV, the key-value store behind the <a href=\"\/tidb\/\">TiDB<\/a> database.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Rust_Compile-time_Adventures_with_TiKV_Episode_2\"><\/span>Rust Compile-time Adventures with TiKV: Episode 2<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In <a href=\"https:\/\/pingcap.com\/blog\/rust-compilation-model-calamity\/\">the previous post in the series<\/a> we covered Rust&#8217;s early development history, and how it led to a series of decisions that resulted in a high-performance language that compiles slowly. Over the next few we&#8217;ll describe in more detail some of the designs in Rust that make compile time slow.<\/p>\n<p>This time, we&#8217;re talking about monomorphization.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Comments_on_the_last_episode\"><\/span>Comments on the last episode<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>After the <a href=\"https:\/\/pingcap.com\/blog\/rust-compilation-model-calamity\/\">previous<\/a> episode of this series, people made a lot of great comments on HackerNews, Reddit, and Lobste.rs.<\/p>\n<p>Some common comments:<\/p>\n<ul>\n<li>The compile times we see for TiKV aren&#8217;t so terrible, and are comparable to<br \/>\nC++.<\/li>\n<li>What often matters is partial rebuilds since that is what developers<br \/>\nexperience most in their build-test cycle.<\/li>\n<\/ul>\n<p>Some subjects I hadn&#8217;t considered:<\/p>\n<ul>\n<li><a href=\"https:\/\/news.ycombinator.com\/item?id=22199471\">WalterBright pointed out<\/a> that data flow analysis (DFA) is expensive<br \/>\n(quadratic). Rust depends on data flow analysis. I don&#8217;t know how this<br \/>\nimpacts Rust compile times, but it&#8217;s good to be aware of.<\/li>\n<li><a href=\"https:\/\/www.reddit.com\/r\/rust\/comments\/ew5wnz\/the_rust_compilation_model_calamity\/fg07hvv\/\">kibwen reminded us<\/a> that faster linkers have an impact on build times,<br \/>\nand that LLD may be faster than the system linker eventually.<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"A_brief_aside_about_compile-time_scenarios\"><\/span>A brief aside about compile-time scenarios<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It&#8217;s tempting to talk about &#8220;compile-time&#8221; broadly, without any further clarification, but there are many types of &#8220;compile-time&#8221;, some that matter more or less to different people. The four main compile-time scenarios in Rust are:<\/p>\n<ul>\n<li>development profile \/ full rebuilds<\/li>\n<li>development profile \/ partial rebuilds<\/li>\n<li>release profile \/ full rebuilds<\/li>\n<li>release profile \/ partial rebuilds<\/li>\n<\/ul>\n<p>The &#8220;development profile&#8221; entails compiler settings designed for fast compile times, slow run times, and maximum debuggability. The &#8220;release profile&#8221; entails compiler settings designed for fast run times, slow compile times, and, usually, minimum debuggability. In Rust, these are invoked with <code>cargo build<\/code> and <code>cargo build --release<\/code> respectively, and are indicative of the compile-time\/run-time tradeoff.<\/p>\n<p>A full rebuild is building the entire project from scratch, and a partial rebuild happens after modifying code in a previously built project. Partial rebuilds can notably benefit from <a href=\"https:\/\/rust-lang.github.io\/rustc-guide\/queries\/incremental-compilation.html\">incremental compilation<\/a>.<\/p>\n<p>In addition to those there are also<\/p>\n<ul>\n<li>test profile \/ full rebuilds<\/li>\n<li>test profile \/ partial rebuilds<\/li>\n<li>bench profile \/ full rebuilds<\/li>\n<li>bench profile \/ partial rebuilds<\/li>\n<\/ul>\n<p>These are mostly similar to development mode and release mode respectively, though the interactions in cargo between development \/ test and release \/ bench can be subtle and surprising. There may be other profiles (TiKV has more), but those are the obvious ones for Rust, as built-in to cargo. Beyond that there are other scenarios, like typechecking only (<code>cargo check<\/code>), building just a single project (<code>cargo build -p<\/code>), single-core vs. multi-core, local vs. distributed, local vs. CI.<\/p>\n<p>Compile time is also affected by human perception \u2014 it&#8217;s possible for compile time to feel bad when it&#8217;s actually decent, and to feel decent when it&#8217;s actually not so great. This is one of the premises behind the <a href=\"https:\/\/github.com\/rust-lang\/rls\">Rust Language Server<\/a> (RLS) and <a href=\"https:\/\/github.com\/rust-analyzer\/rust-analyzer\">rust-analyzer<\/a> \u2014 if developers are getting constant, real-time feedback in their IDE then it doesn&#8217;t matter as much how long a full compile takes.<\/p>\n<p>So it&#8217;s important to keep in mind through this series that there is a spectrum of tunable possibilities from &#8220;fast compile \/ slow run&#8221; to &#8220;fast run \/ slow compile&#8221;, there are different scenarios that affect compile time in different ways, and in which compile time affects perception in different ways.<\/p>\n<p>It happens that for TiKV we&#8217;ve identified that the scenario we care most about with respect to compile time is &#8220;release profile \/ partial rebuilds&#8221;. More about that in future installments.<\/p>\n<p>The rest of this post details some of the major designs in Rust that cause slow compile time. I describe them as &#8220;tradeoffs&#8221;, as there are good reasons Rust is the way it is, and language design is full of awkward tradeoffs.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Monomorphized_generics\"><\/span>Monomorphized generics<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Rust&#8217;s approach to generics is the most obvious language feature to blame on bad compile times, and understanding how Rust translates generic functions to machine code is important to understanding the Rust compile-time\/run-time tradeoff.<\/p>\n<p>Generics generally are a complex topic, and Rust generics come in a number of forms. Rust has generic functions and generic types, and they can be expressed in multiple ways. Here I&#8217;m mostly going to talk about how Rust calls generic functions, but there are further compile-time considerations for generic type translations. I ignore other forms of generics (like <code>impl Trait<\/code>), as either they have similar compile-time impact, or I just don&#8217;t know enough about them.<\/p>\n<p>As a simple example for this section, consider the following <code>ToString<\/code> trait and the generic function <code>print<\/code>:<\/p>\n<pre><code class=\"language-rust\">use std::string::ToString;\n\nfn print&lt;T: ToString&gt;(v: T) {\n     println!(\"{}\", v.to_string());\n}\n<\/code><\/pre>\n<p><code>print<\/code> will print to the console anything that can be converted to a <code>String<\/code> type. We say that &#8220;<code>print<\/code> is generic over type <code>T<\/code>, where <code>T<\/code> implements <code>Stringify<\/code>&#8220;. Thus I can call <code>print<\/code> with different types:<\/p>\n<pre><code class=\"language-rust\">fn main() {\n    print(\"hello, world\");\n    print(101);\n}\n<\/code><\/pre>\n<p>The way a compiler translates these calls to <code>print<\/code> to machine code has a huge impact on both the compile-time and run-time characteristics of the language.<\/p>\n<p>When a generic function is called with a particular set of type parameters it is said to be <em>instantiated<\/em> with those types.<\/p>\n<p>In general, for programming languages, there are two ways to translate a generic function:<\/p>\n<ol>\n<li>translate the generic function for each set of instantiated type parameters, calling each trait method directly, but duplicating most of the generic function&#8217;s machine instructions, or<\/li>\n<li>translate the generic function just once, calling each trait method through a function pointer (via a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Virtual_method_table\">&#8220;vtable&#8221;<\/a>).<\/li>\n<\/ol>\n<p>The first results in <em>static<\/em> method dispatch, the second in <em>dynamic<\/em> (or &#8220;virtual&#8221;) method dispatch. The first is sometimes called &#8220;monomorphization&#8221;, particularly in the context of C++ and Rust, a confusingly complex word for a simple idea.<\/p>\n<h3>An example in Rust<\/h3>\n<p>The previous example uses Rust&#8217;s type parameters (<code>&lt;T: ToString&gt;<\/code>) to define a<br \/>\nstatically-dispatched <code>print<\/code> function. In this section we present two more Rust<br \/>\nexamples, the first with static dispatch, using references to <code>impl<\/code> trait<br \/>\ninstances, and the second with dynamic dispatch, with references to <code>dyn<\/code> trait<br \/>\ninstances.<\/p>\n<p>Static (<a href=\"https:\/\/play.rust-lang.org\/?version=stable&amp;mode=release&amp;edition=2018&amp;gist=066e72731fbdbf212f68c25b5a4e3b72\">playground link<\/a>):<\/p>\n<pre><code class=\"language-rust\">use std::string::ToString;\n\n#[inline(never)]\nfn print(v: &amp;impl ToString) {\n     println!(\"{}\", v.to_string());\n}\n\nfn main() {\n    print(&amp;\"hello, world\");\n    print(&amp;101);\n}\n\n<\/code><\/pre>\n<p>Dynamic (<a href=\"https:\/\/play.rust-lang.org\/?version=stable&amp;mode=release&amp;edition=2018&amp;gist=d359d0440acaeed1d25020955979b9ce\">playground link<\/a>):<\/p>\n<pre><code class=\"language-rust\">use std::string::ToString;\n\n#[inline(never)]\nfn print(v: &amp;dyn ToString) {\n     println!(\"{}\", v.to_string());\n}\n\nfn main() {\n    print(&amp;\"hello, world\");\n    print(&amp;101);\n}\n<\/code><\/pre>\n<p>Notice that the only difference between these two cases is that the first<br \/>\n<code>print<\/code>&#8216;s argument is type <code>&amp;impl ToString<\/code>, and the second&#8217;s is <code>&amp;dyn ToString<\/code>. The first is using static dispatch, and the second dynamic.<\/p>\n<p>In Rust <code>&amp;impl ToString<\/code> is essentially shorthand for a type parameter argument<br \/>\nthat is only used once, like in the earlier example <code>fn print&lt;T: ToString&gt;(v: T)<\/code>.<\/p>\n<p>Note that in these examples we have to use <code>inline(never)<\/code> to defeat the<br \/>\noptimizer. Without this it would turn these simple examples into the exact same<br \/>\nmachine code. I&#8217;ll explore this phenomenon further in a future episode of this<br \/>\nseries.<\/p>\n<p>Below is an extremely simplified and sanitized version of the assembly<br \/>\nfor these two examples. If you want to see the real thing, the playground<br \/>\nlinks above can generate them by clicking the buttons labeled <code>... -&gt; ASM<\/code>.<\/p>\n<pre><code>print::hffa7359fe88f0de2:\n    ...\n    callq   *::core::fmt::write::h01edf6dd68a42c9c(%rip)\n    ...\n\nprint::ha0649f845bb59b0c:\n    ...\n    callq   *::core::fmt::write::h01edf6dd68a42c9c(%rip)\n    ...\n\nmain::h6b41e7a408fe6876:\n    ...\n    callq   print::hffa7359fe88f0de2\n    ...\n    callq   print::ha0649f845bb59b0c\n<\/code><\/pre>\n<p>And the dynamic case:<\/p>\n<pre><code>print::h796a2cdf500a8987:\n    ...\n    callq   *::core::fmt::write::h01edf6dd68a42c9c(%rip)\n    ...\n\nmain::h6b41e7a408fe6876:\n    ...\n    callq   print::h796a2cdf500a8987\n    ...\n    callq   print::h796a2cdf500a8987\n<\/code><\/pre>\n<p>The important thing to note here is the duplication of functions or lack<br \/>\nthereof, depending on the strategy. In the static case there are two <code>print<\/code><br \/>\nfunctions, distinguished by a hash value in their names, and <code>main<\/code> calls both<br \/>\nof them. In the dynamic case, there is a single <code>print<\/code> function that <code>main<\/code><br \/>\ncalls twice. The details of how these two strategies actually handle their<br \/>\narguments at the machine level are too intricate to go into here.<\/p>\n<h3>More about the tradeoff<\/h3>\n<p>These two strategies represent a notoriously difficult tradeoff: the first creates lots of machine instruction duplication, forcing the compiler to spend time generating those instructions, and putting pressure on the instruction cache, but \u2014 crucially \u2014 dispatching all the trait method calls statically instead of through a function pointer. The second saves lots of machine instructions and takes less work for the compiler to translate to machine code, but every trait method call is an indirect call through a function pointer, which is generally slower because the CPU can&#8217;t know what instruction it is going jump to until the pointer is loaded.<\/p>\n<p>It is often thought that the static dispatch strategy results in faster machine code, though I have not seen any research into the matter (we&#8217;ll do an experiment on this subject in a future edition of this series). Intuitively, it makes sense \u2014 if the CPU knows the address of all the functions it is calling it should be able to call them faster than if it has to first load the address of the function, then load the instruction code into the instruction cache. There are though factors that make this intuition suspect:<\/p>\n<ul>\n<li>first, modern CPUs have invested a lot of silicon into branch prediction, so<br \/>\nif a function pointer has been called recently it will likely be predicted<br \/>\ncorrectly the next time and called quickly;<\/li>\n<li>second, monomorphization results in huge quantities of machine instructions, a<br \/>\nphenomenon commonly referred to as &#8220;code bloat&#8221;, which could put great<br \/>\npressure on the CPU&#8217;s instruction cache;<\/li>\n<li>third, the LLVM optimizer is surprisingly smart, and with enough visibility<br \/>\ninto the code can sometimes turn virtual calls into static calls.<\/li>\n<\/ul>\n<p>C++ and Rust both strongly encourage monomorphization, both generate some of the fastest machine code of any programming language, and both have problems with code bloat. This seems to be evidence that the monomorphization strategy is indeed the faster of the two. There is though a curious counter-example: C. C has no generics at all, and C programs are often both the slimmest <em>and<\/em> fastest in their class. Reproducing the monomorphization strategy in C requires using ugly C macro preprocessor techniques, and modern object-orientation patterns in C are often vtable-based.<\/p>\n<p><em>Takeaway: it is a broadly thought by compiler engineers that monomorphiation results in somewhat faster generic code while taking somewhat longer to compile.<\/em><\/p>\n<p>Note that the monomorphization-compile-time problem is compounded in Rust because Rust translates generic functions in every crate (generally, &#8220;compilation unit&#8221;) that instantiates them. That means that if, given our <code>print<\/code> example, crate <code>a<\/code> calls <code>print(\"hello, world\")<\/code>, and crate <code>b<\/code> also calls <code>print(\"hello, world, or whatever\")<\/code>, then both crate <code>a<\/code> and <code>b<\/code> will contain the monomorphized <code>print_str<\/code> function \u2014 the compiler does all the type-checking and translation work twice. This is partially mitigated today at lower optimization levels by <a href=\"https:\/\/github.com\/rust-lang\/rust\/issues\/47317#issuecomment-478894318\">shared generics<\/a>, though there are still duplicated generics <a href=\"https:\/\/github.com\/rust-lang\/rust\/pull\/48779\">in sibling dependencies<\/a>,<br \/>\nand at higher optimization levels.<\/p>\n<p>All that is only touching on the surface of the tradeoffs involved in monomorphization. I passed this draft by <a href=\"https:\/\/github.com\/nikomatsakis\">Niko<\/a>, the primary type theorist behind Rust, and he had some words to say about it:<\/p>\n<blockquote><p>niko: so far, everything looks pretty accurate, except that I think the monomorphization area leaves out a lot of the complexity. It&#8217;s definitely not just about virtual function calls.<\/p><\/blockquote>\n<blockquote><p>niko: it&#8217;s also things like <code>foo.bar<\/code> where the offset of bar depends on the type of foo<\/p><\/blockquote>\n<blockquote><p>niko: many languages sidestep this problem by using pointers everywhere (including generic C, if you don&#8217;t use macros)<\/p><\/blockquote>\n<blockquote><p>niko: not to mention the construction of complex types like iterators, that are basically mini-programs fully instantiated and then customizable &#8212; though this <em>can<\/em> be reproduced by a sufficiently smart compiler<\/p><\/blockquote>\n<blockquote><p>niko: (in particular, virtual calls can be inlined too, though you get less optimization; I remember some discussion about this at the time &#8230; how virtual call inlining happens relatively late in the pipeline)<\/p><\/blockquote>\n<blockquote><p>brson: does the field offset issue only come in to play with associated types?<\/p><\/blockquote>\n<blockquote><p>niko: no<\/p><\/blockquote>\n<blockquote><p>niko: <code>struct Foo&lt;T&gt; { x: u32, y: T, z: f64 }<\/code><\/p><\/blockquote>\n<blockquote><p>niko: <code>fn bar&lt;T&gt;(f: Foo&lt;T&gt;) -&gt; f64 { f.z }<\/code><\/p><\/blockquote>\n<blockquote><p>niko: as I recall, before we moved to monomorphization, we had to have two paths for everything: the easy, static path, where all types were known to LLVM, and the horrible, dynamic path, where we had to generate the code to dynamically compute the offsets of fields and things<\/p><\/blockquote>\n<blockquote><p>niko: unsurprisingly, the two were only rarely in sync<\/p><\/blockquote>\n<blockquote><p>niko: which was a common source of bugs<\/p><\/blockquote>\n<blockquote><p>niko: I think a lot of this could be better handled today &#8212; we have e.g. a reasonably reliable bit of code that computes Layout, we have MIR which is a much simpler target &#8212; so I am not as terrified of having to have those two paths<\/p><\/blockquote>\n<blockquote><p>niko: but it&#8217;d still be a lot of work to make it all work<\/p>\n<p>niko: there was also stuff like the need to synthesize type descriptors on the fly (though maybe we could always get by with stack allocation for that)<\/p><\/blockquote>\n<blockquote><p>niko: e.g., <code>fn foo&lt;T&gt;() { bar::&lt;Vec&lt;T&gt;&gt;(); } fn bar&lt;U&gt;() { .. }<\/code><\/p><\/blockquote>\n<blockquote><p>niko: here, you have a type descriptor for T that was given to you dynamically, but you have to build the type descriptor for Vec&lt;T&gt;<\/p><\/blockquote>\n<blockquote><p>niko: and then we can make it even worse<\/p><\/blockquote>\n<blockquote><p>niko: <code>fn foo&lt;T&gt;() { bar::&lt;Vec&lt;T&gt;&gt;(); } fn bar&lt;U: Debug&gt;() { .. }<\/code><\/p><\/blockquote>\n<blockquote><p>niko: now we have to reify all the IMPLS of Debug<\/p><\/blockquote>\n<blockquote><p>niko: so that we can do trait matching at runtime<\/p><\/blockquote>\n<blockquote><p>niko: because we have to be able to figure out <code>Vec&lt;T&gt;: Debug<\/code>, and all we know is <code>T: Debug<\/code><\/p><\/blockquote>\n<blockquote><p>niko: we might be able to handle that by bubbling up the Vec&lt;T&gt; to our callers&#8230;<\/p><\/blockquote>\n<h2><span class=\"ez-toc-section\" id=\"In_the_next_episode_of_Rust_Compile-time_Adventures_with_TiKV\"><\/span>In the next episode of Rust Compile-time Adventures with TiKV<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In the next episode of this series we&#8217;ll discuss <a href=\"https:\/\/pingcap.com\/blog\/rust-huge-compilation-units\">compilation units<\/a> &#8212; the bundles of code that a compiler processes at a single time &#8212; and how selecting compilation units affects compile time.<\/p>\n<p>Stay Rusty, friends.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Thanks\"><\/span>Thanks<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A number of people helped with this blog series. Thanks especially to Niko Matsakis for the feedback, and Calvin Weng for proofreading and editing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the second episode of the Rust Compile Time series. Brian Anderson, one of Rust&#8217;s original authors, talks about monomorphization, using the TiKV project as a case study.<\/p>","protected":false},"author":43,"featured_media":446,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ub_ctt_via":"","footnotes":""},"categories":[6],"tags":[23,22],"class_list":["post-444","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-rust","tag-tikv"],"acf":[],"featured_image_src":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","author_info":{"display_name":"Brian Anderson","author_link":"https:\/\/www.pingcap.com\/ko\/blog\/author\/brian-anderson\/"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.9 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Generics and Compile-Time in Rust | TiDB<\/title>\n<meta name=\"description\" content=\"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post &quot;Generics and Compile-Time in Rust&quot; here.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/\" \/>\n<meta property=\"og:locale\" content=\"ko_KR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Generics and Compile-Time in Rust | TiDB\" \/>\n<meta property=\"og:description\" content=\"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post &quot;Generics and Compile-Time in Rust&quot; here.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/\" \/>\n<meta property=\"og:site_name\" content=\"TiDB\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/facebook.com\/pingcap2015\" \/>\n<meta property=\"article:published_time\" content=\"2020-06-15T00:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-06-03T13:14:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"667\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Brian Anderson\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:site\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Brian Anderson\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11\ubd84\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/\"},\"author\":{\"name\":\"Brian Anderson\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/6883a92f64d13c80ee57193b8c291a1c\"},\"headline\":\"Generics and Compile-Time in Rust\",\"datePublished\":\"2020-06-15T00:00:00+00:00\",\"dateModified\":\"2024-06-03T13:14:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/\"},\"wordCount\":2192,\"publisher\":{\"@id\":\"https:\/\/www.pingcap.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\",\"keywords\":[\"Rust\",\"TiKV\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"ko-KR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/\",\"url\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/\",\"name\":\"Generics and Compile-Time in Rust | TiDB\",\"isPartOf\":{\"@id\":\"https:\/\/www.pingcap.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\",\"datePublished\":\"2020-06-15T00:00:00+00:00\",\"dateModified\":\"2024-06-03T13:14:20+00:00\",\"description\":\"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post \\\"Generics and Compile-Time in Rust\\\" here.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#breadcrumb\"},\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage\",\"url\":\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\",\"width\":2000,\"height\":667},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.pingcap.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Generics and Compile-Time in Rust\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.pingcap.com\/#website\",\"url\":\"https:\/\/www.pingcap.com\/\",\"name\":\"TiDB\",\"description\":\"TiDB | SQL at Scale\",\"publisher\":{\"@id\":\"https:\/\/www.pingcap.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.pingcap.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ko-KR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.pingcap.com\/#organization\",\"name\":\"PingCAP\",\"url\":\"https:\/\/www.pingcap.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png\",\"width\":811,\"height\":232,\"caption\":\"PingCAP\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/facebook.com\/pingcap2015\",\"https:\/\/x.com\/PingCAP\",\"https:\/\/linkedin.com\/company\/pingcap\",\"https:\/\/youtube.com\/channel\/UCuq4puT32DzHKT5rU1IZpIA\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/6883a92f64d13c80ee57193b8c291a1c\",\"name\":\"Brian Anderson\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg\",\"caption\":\"Brian Anderson\"},\"description\":\"Senior Database Engineer\",\"url\":\"https:\/\/www.pingcap.com\/ko\/blog\/author\/brian-anderson\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Generics and Compile-Time in Rust | TiDB","description":"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post \"Generics and Compile-Time in Rust\" here.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/","og_locale":"ko_KR","og_type":"article","og_title":"Generics and Compile-Time in Rust | TiDB","og_description":"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post \"Generics and Compile-Time in Rust\" here.","og_url":"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/","og_site_name":"TiDB","article_publisher":"https:\/\/facebook.com\/pingcap2015","article_published_time":"2020-06-15T00:00:00+00:00","article_modified_time":"2024-06-03T13:14:20+00:00","og_image":[{"width":2000,"height":667,"url":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","type":"image\/jpeg"}],"author":"Brian Anderson","twitter_card":"summary_large_image","twitter_creator":"@PingCAP","twitter_site":"@PingCAP","twitter_misc":{"Written by":"Brian Anderson","Est. reading time":"11\ubd84"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#article","isPartOf":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/"},"author":{"name":"Brian Anderson","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/6883a92f64d13c80ee57193b8c291a1c"},"headline":"Generics and Compile-Time in Rust","datePublished":"2020-06-15T00:00:00+00:00","dateModified":"2024-06-03T13:14:20+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/"},"wordCount":2192,"publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","keywords":["Rust","TiKV"],"articleSection":["Engineering"],"inLanguage":"ko-KR"},{"@type":"WebPage","@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/","url":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/","name":"Generics and Compile-Time in Rust | TiDB","isPartOf":{"@id":"https:\/\/www.pingcap.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","datePublished":"2020-06-15T00:00:00+00:00","dateModified":"2024-06-03T13:14:20+00:00","description":"Learn about the benefits of TiDB and the TiDB Cloud solutions from PingCAP. Read our latest post \"Generics and Compile-Time in Rust\" here.","breadcrumb":{"@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#breadcrumb"},"inLanguage":"ko-KR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/"]}]},{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#primaryimage","url":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","contentUrl":"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg","width":2000,"height":667},{"@type":"BreadcrumbList","@id":"https:\/\/www.pingcap.com\/blog\/generics-and-compile-time-in-rust\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.pingcap.com\/"},{"@type":"ListItem","position":2,"name":"Generics and Compile-Time in Rust"}]},{"@type":"WebSite","@id":"https:\/\/www.pingcap.com\/#website","url":"https:\/\/www.pingcap.com\/","name":"\ud2f0DB","description":"TiDB | SQL at Scale","publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.pingcap.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ko-KR"},{"@type":"Organization","@id":"https:\/\/www.pingcap.com\/#organization","name":"PingCAP","url":"https:\/\/www.pingcap.com\/","logo":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/","url":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","contentUrl":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","width":811,"height":232,"caption":"PingCAP"},"image":{"@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/facebook.com\/pingcap2015","https:\/\/x.com\/PingCAP","https:\/\/linkedin.com\/company\/pingcap","https:\/\/youtube.com\/channel\/UCuq4puT32DzHKT5rU1IZpIA"]},{"@type":"Person","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/6883a92f64d13c80ee57193b8c291a1c","name":"Brian Anderson","image":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/image\/","url":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","contentUrl":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","caption":"Brian Anderson"},"description":"Senior Database Engineer","url":"https:\/\/www.pingcap.com\/ko\/blog\/author\/brian-anderson\/"}]}},"grav_blocks":false,"card_markup":"<a class=\"card-resource bg-white\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/generics-and-compile-time-in-rust\/\"><div class=\"card-resource__image-container\"><img class=\"card-resource__image\" alt=\"rust-compile-time-adventures.png\" src=\"https:\/\/static.pingcap.com\/files\/2020\/06\/rust-compile-time-adventures.jpg\" loading=\"lazy\" width=2000 height=667 \/><\/div><div class=\"card-resource__content-container\"><div class=\"card-resource__content-head\"><div class=\"card-resource__category\">Engineering<\/div><\/div><h5 class=\"card-resource__title\">Generics and Compile-Time in Rust<\/h5><\/div><\/a>","_links":{"self":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/444","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/users\/43"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/comments?post=444"}],"version-history":[{"count":3,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/444\/revisions"}],"predecessor-version":[{"id":17452,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/444\/revisions\/17452"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media\/446"}],"wp:attachment":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media?parent=444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/categories?post=444"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/tags?post=444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}