Fullstack Rust (Nate Murray) (Z-Library)
Author: Nate Murray
商业
No Description
📄 File Format:
PDF
💾 File Size:
2.1 MB
18
Views
0
Downloads
0.00
Total Donations
📄 Text Preview (First 20 pages)
ℹ️
Registered users can read the full content for free
Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.
📄 Page
1
(This page has no text content)
📄 Page
2
Fullstack Rust The Complete Guide to Buildings Apps with the Rust Programming Language and Friends Written by Andrew Weiss Edited by Nate Murray © 2020 Fullstack.io All rights reserved. No portion of the book manuscript may be reproduced, stored in a retrieval system, or transmitted in any form or by any means beyond the number of purchased copies, except for a single backup or archival copy. The code may be used freely in your projects, commercial or otherwise. The authors and publisher have taken care in preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damagers in connection with or arising out of the use of the information or programs container herein. Published by Fullstack.io.
📄 Page
3
Contents Book Revision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Join Our Discord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Be notified of updates via Twitter . . . . . . . . . . . . . . . . . . . . . . . . . 1 We’d love to hear from you! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Why Rust? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Why not Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 This book’s mission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Setting expectations based on your background . . . . . . . . . . . . . . . . 10 Getting your environment setup . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Rustup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Cargo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 IDEs, RLS, Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Clippy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Rustfmt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 The Nomicon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Making Your First Rust App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Binary vs. library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 The generated project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Crates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Making our crate a library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
📄 Page
4
CONTENTS Trade-offs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Print a list of numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Testing our code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Wrapping up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Making A Web App With Actix . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Web Ecosystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Starting out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Handling our first request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Adding State to Our Web App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Recap and overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Adding state . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Receiving input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Custom error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Handling path variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Wrapping up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Even More Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Crates to know . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Building a blog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Building the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 Extending our application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Adding routes for posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Extending further: comments . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 Adding routes for comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Create a post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Create a post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Publish a post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Comment on a post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 List all posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 See posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 Publish other post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
📄 Page
5
CONTENTS List all posts again . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 See users comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 See post comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 Wrapping up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 What is Web Assembly? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Intro to Web Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Rust in the browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 The Smallest Wasm Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 Working with primatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 Working with complex types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 The Real Way to Write Wasm . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Other Wasm Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Command Line Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Initial setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Making an MVP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Recap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Adding a configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Adding sessions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 Syntax highlighting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 Declarative Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 Procedural Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Writing a custom derive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Using our custom derive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Wrapping up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Revision 4 (02-19-2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Revision 3 (01-29-2020) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Revision 2 (11-25-2019) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Revision 1 (10-29-2019) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
📄 Page
6
CONTENTS 1 Book Revision Revision 4 - First Edition - 2020-02-19 Join Our Discord Come chat with other readers of the book in the official newline Discord channel: Join here: https://newline.co/discord/rust¹ Bug Reports If you’d like to report any bugs, typos, or suggestions just email us at:us@fullstack.io. Be notified of updates via Twitter If you’d like to be notified of updates to the book on Twitter, follow us at @full- stackio². We’d love to hear from you! Did you like the book? Did you find it helpful? We’d love to add your face to our list of testimonials on the website! Email us at: us@fullstack.io³. ¹https://newline.co/discord/rust ²https://twitter.com/fullstackio ³mailto:us@fullstack.io
📄 Page
7
Introduction There are numerous reasons to be hopeful about the future of computing, one of which is the existence and continued progression of the Rust programming language. We are currently in the fifth era of programming language evolution. This is an era where languages have been able to take all of the learnings since the 1950s and incorporate the best parts into languages each with its own cohesive vision. We have specialized languages cropping up for a wide variety of tasks and countless general purpose languages being actively developed and used. There are significant resources in industry to invest in language design and development which compli- ment the vibrant academic community. With tools like LLVM and the explosion of open source, creating a language has never been easier. It is in this environment that Rust has been voted the “most loved programming language” in the Stack Overflow Developer Survey every year since 2016. Standing out in this increasingly crowded world of languages is enough of a reason to ask why Rust? Why Rust? There are a few potential readings of this question: why should I learn Rust, why are others using Rust, why should I choose Rust over language X? These are all relevant, but I want to start with a bit of a philosophical argument for Rust independent of these specific points. There is a limit to how transformative an experience you can have when learning a language in a similar paradigm to one you already know. Every language and paradigm has an intrinsic style that is forced on you as you try to solve problems. If you work within that style then your code will flow naturally and the language will feel like it is working with you. On the other hand, if you fight the natural style of the language you will find it hard or impossible to express your ideas.
📄 Page
8
Introduction 2 Moreover, learning and working with a language will teach you ways to be more effective based on how the language guides you based on its natural design. How much you are able to learn is a function of how much your prior experience and mental models cover the new language. Rust borrows a lot of ideas from other languages and is truly multi-paradigm, meaning you can write mostly functional code or mostly imperative code and still fit nicely within the language. The most unique feature of the language, the borrow checker, is a system that enforces certain invariants which allow you to make certain safety guarantees. Even this is built on prior art found in earlier languages. All of these good ideas from the world of programming language design combine in a unique way to make Rust a language that truly makes you think about writing code from a novel perspective. It does not matter how much experience you have, learning Rust will forever change the way you write code for the better. Okay with that philosophical argument out of the way, let’s dig in to some specifics of why Rust is a exciting. To help guide this discussion, we can break things down into a few broad categories. On language comparisons There is no best programming language. Almost every task has a variety of languages which could be the right tool. Every language comes with good parts and bad parts. Evaluating these trade-offs when faced with a particular problem space is an art unto itself. Therefore, nothing in this book is intended to disparage or denigrate any particular alternative language. The primary goal of this book is to faithfully present Rust. That being said, sometimes comparisons with other languages are instructive and are meant to be instructive rather than as fuel in a flame war. Language features There are a lot of features of Rust which make it a great tool for a great number of tasks. Some highlights include: • Performance
📄 Page
9
Introduction 3 • Strong, static, expressive type system • Great error messages • Modern generics • Memory safety • Fearless concurrency • Cross platform • C interoperability Let’s briefly go through some of these which are probably the biggest reasons that Rust gets talked about. Performance Rust is exceptionally fast, in the same ballpark as C and C++. For some programs, specifically due to the lack of pointer aliasing, the Rust compiler can sometimes have enough information to optimize code to be faster than what is possible in C without directly writing assembly. For the vast majority of use cases, you should consider Rust to be fast enough. Often the most obvious way to write a program is also the fastest. Part of this comes from the commitment to zero-cost abstractions, which are summarized by Bjarne Stroustrup, the creator of C++, as: What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better. Most of the abstractions in Rust, for example iterators, are zero-cost by this definition. The most efficient way to traverse a vector of data is to use a for loop which uses an iterator trait. The generated assembly is usually as good as you could hope for had you written it by hand. The other aspect of performance is memory consumption. Rust does not have a garbage collector so you can use exactly as much memory as is strictly necessary at any given time. Due to the design of the language, you start to think and see every memory allocation. Using less memory is often easier than the converse. The rest of the language is designed around making working without a garbage collector painless.
📄 Page
10
Introduction 4 Type system The type system of Rust is influenced by the long lineage of functional programming languages such as ML and Haskell. It is static, nominal, strong, and for the most part inferred. Don’t worry if that didn’t mean anything to you, but if it did then great. You encode the ideas and constraints of your problemwith types. You only have to specify types in a few places with the rest able to be inferred. The compiler then checks everything for you so that you get faster feedback about potential problems. Entire classes of bugs are impossible because of static typing. Most things you encounter in practice are expressible in the type system. The compiler then checks everything for you so that you get faster feedback about potential problems. Entire classes of bugs are impossible because of static typing. A type system is often called expressive if it is easy to encode your ideas. There are some concepts which are impossible to express in static type systems. Rust has powerful abstraction facilities like sum and product types, tuples, generics, etc. which put the type system definitely in the expressive camp. Memory safety A language is memory safe if certain classes of bugs related to memory access are not possible. A language can be called memory unsafe if certain bugs are possible. A non-exhaustive list of memory related bugs include: dereferencing null pointers, use-after free, dangling pointers, buffer overflows. If you have never written code in a memory unsafe language then these might sound like gibberish to you, which is fine. The important point is this class of bugs is a consistent and large source of security vulnerabilities in systems implemented with memory unsafe languages. For example, about 20% of CVEs⁴ ever filed against the Linux kernel are due to memory corruption or overflows. Linux is implemented primarily in C, a spectacularly memory unsafe language. Memory safety bugs are bad for security and reliability. They lead to vulnerabilities and they lead to crashes. If you can rule these out at compile time then you are in a much better state of the world. Rust is designed to be memory safe, and thus it does not permit null pointers, dangling pointers, or data races in safe code. There are many interacting features ⁴https://www.cvedetails.com/product/47/Linux-Linux-Kernel.html?vendor_id=33
📄 Page
11
Introduction 5 which allow this guarantee. The primary one is the unique system of ownership combined with the borrow checker. This is part of the compiler that ensures pieces of data live at least as long as they need to in order to be alive when they are used. One other feature is the builtin Option type. This is used to replace the concept of null found in many other languages. In some languages, every type is secretly the union of that type with null. This means that you can always end up with bugs where you assume some variable had a value and it actually was inhabited by the dreaded null. Rust disallows this by not having null and instead having a type which can explicitly wrap other types. For example, consider this Rust code: fn print_number(num: Option<i32>) { match num { Some(n) => println!("I see {}!", n), None => println!("I see nothing!"), } } fn main() { let x = Some(42); let y = None; print_number(x); print_number(y); } The function print_number must handle the case where num is None, meaning the Option has no value. There are a few different ways to handle that case but you must explicitly do something for that case or else your code will not compile. The one caveat here is that Rust does allow blocks of code to be marked unsafe and within those blocks it is possible to violatememory safety. Some things are impossible for the compiler to verify are safe and therefore it refuses to do so. It requires you to use unsafe regions of code to ensure that you understand the invariants required to make sure your code truly is safe. This does not defeat the purpose, rather in isolates the areas of auditability to just those sections of code which are specifically marked. Nothing you do in normal Rust,
📄 Page
12
Introduction 6 also called safe Rust, can result in a memory safety violation, unless something in unsafe code did something wrong ahead of you. As an example, calling C functions from Rust is unsafe. This is because Rust has no way of knowing what the C code is doing, and C is inherently unsafe, therefore the compiler cannot uphold its guarantees if you call out to C. However, can it be safe to call C? Yes, provided you fill in the visibility gap for the compiler with your own logic. Fearless concurrency Concurrency in programming means that multiple tasks can be worked on at the same time. This is possible even for a single thread of execution by interleaving the work for different tasks in chunks rather than only working on tasks as entire chunks. Parallelism in programming means multiple tasks executing at the exact same time. True parallelism requires multiple threads of execution (or the equivalent). The Rust language describes its facilities for concurrent and parallel computing as fearless concurrency with a bit of conflation of terms. I will continue in this tradition and use concurrency to mean concurrency and/or parallelism. Most modern, high level languages have chosen how they want to support concur- rency and mostly force you down that path. Some more general purpose languages provide the tools to handle concurrency however you see fit. For example, Go is designed around Communicating Sequential Processes (CSP) and therefore concur- rency is most easily achieved using channels and goroutines. Python, on the other hand, has libraries for threads, multiprocesses, message passing actors, etc. Rust is a low-level language by design and therefore provides tools that allow you to use the model of your choice to achieve your particular goals. Therefore, there are facilities for threads but also channels and message passing. Regardless of what technique you choose to tackle concurrency and/or parallelism, the same ownership model and type system that ensures memory safety also ensures thread safety. This means that it is a compile time error to write to the same memory from different threads without some form of synchronization. The details are less important than the concept that entire classes of problems that are notoriously difficult to debug in other languages are completely eliminated at compile timewhile, importantly, retaining all of the performance benefits.
📄 Page
13
Introduction 7 C interoperability Rust is foremost a systems programming language. That means it is designed for building low level systems with strict performance requirements and reliability constraints. Frequently in this world, C is the glue that bindsmany disparate systems. Therefore being able to interoperate with C is an absolute necessity to be able to have a serious systems language. Luckily it is straightforward to interact with C both by calling into C from Rust, as well as exposing Rust as a C library. Youmight be saying that sounds great but I don’t plan onwriting an operating system anytime soon so why should I care? C is also the most common mechanism for making dynamic languages faster. Typically, when parts of your Python or Ruby code are showing performance problems, you can reach for an extension written in C to speed things up. Well, now you can write that extension in Rust and get all of the high level benefits of Rust and still make your Python or Ruby code think it is talking to C. This is also quite an interesting area for interacting with the JVM. Ecosystem Software is not constructed in a vacuum, the practice of programming is often a community driven endeavor. Every language has a community whether it actively cultivates it or not. The ecosystem around a language includes the community of people, but also the tooling or lack thereof. Rust has grown quite a lot in its short life and has gone through some growing pains as a result. However, the community has always been very welcoming and importantly the culture is a first-class citizen. Rust specifically has a community team as part of the governance structure of the language. This goes a long way to helping the language and ecosystem grow and mature. We will cover much of the useful tooling that exists around Rust in detail below. However, suffice it to say that the tooling around the language is some of the best that exists. There have been a lot of learnings over the past twenty years about how to manage toolchains and dependencies and Rust has incorporated all of this quite well.
📄 Page
14
Introduction 8 The nature of programming The systems and applications we are building today are different than 50 years ago, they are even different than 10 years ago. Therefore, it should not be too much of a stretch to say that the tools we use should also be different. There is an explosion of embedded systems due to what is commonly called the Internet of Things. However, is C still the best tool for that job? Mission critical software that controls real objects that could lead to serious consequences in the case of failure should be using the best tool for the job. Rust is a serious contender in this space. For example, it is easy to turn off dynamic memory allocation while still being able to use a lot of the nice parts of the language. The other explosion is continuing on the web. We have been in a web revolution for quite a while now, but things have not slowed down. The deficits of JavaScript are well known and have been addressed along quite a few paths. We have many languages which compile to JavaScript but provide nice features like type systems or a functional paradigm. However, there are fundamental performance and security issues with JavaScript regardless of how you generate it. WebAssembly (WASM) is a step in a different direction where we can compile languages like Rust to a format natively executable in the browser. Fun Rust is fun to write. You will disagree with this and think I am crazy at some point while you are learning Rust. There is a learning curve which can be distinctly not fun. However, once your mental model starts to shift, you will find yourself having moments of pure joy when your code just works after the compiler gives you the okay. Why not Rust Rust is just another programming language and as such is just another software project. This means it has built up some legacy, it has some hairy parts, and it has some future plans which may or may not ever happen. Some of this means that for any given project, Rust might not be the right tool for the job.
📄 Page
15
Introduction 9 One area in which Rust might not be right is when interfacing with large C++ codebases. It is possible to have C++ talk to C and then have C talk to Rust and vice versa. That is the approach you should take today if possible. However, Rust does not have a stable ABI nor a stable memory model. Hence, it is not directly compatible with C++. You can incrementally replace parts of a system with Rust and you can build new parts in Rust, but plug-and-play interoperability with C++ is not a solved problem. Furthermore, Rust takes time to learn. Now this is often cited as a reason for sticking with some other language because one is deemed an expert in that language. However, a counter point might be that you are not as much of an expert in that language as you might believe. A further counter point is that it might not matter, the other language might be fundamentally flawed enough that being an expert is irrelevant. Nonetheless, there are times where using the tool you know is the right answer. The gap between learning Rust and knowing it from using it in anger is a bit bigger than in some other languages. Therefore the learning curve might seem steeper than you are used to. However, this is primarily because what is safe in Rust with the borrow checker helping you can be insane in other languages. Type systems are amazing. You tell the computer some facts about your problem domain and it continually checks that those things are true and lets you know if you screw up. Yet there are valid programswhich are inexpressible in a static type system. This is both theoretically true and actually happens in practice. Moreover, dynamic languages can frequently be more productive for small, isolated tasks. Sometimes the cost of the type system is not worth it. This book’s mission Rust has a great set of documentation around the standard library and has an official “book”⁵ which is a great place to start if you are looking for another source of material. However, this book has a different focus than a traditional book trying to teach you a language. Our goal is to build realistic applications and explore some of the techniques and tools available in Rust for accomplishing those tasks. ⁵https://doc.rust-lang.org/book/
📄 Page
16
Introduction 10 In the process of working through some common scenarios, hopefully you will also be able to learn Rust. There is a gradual ramp up from very simple to more complex programs as we build up our Rust toolbelt. One specific goal is to show places where many people usually stumble and try to support you in finding your own ways over those hurdles. This should empower youwhen you branch out to your own problems. This approach has the downside of not necessarily covering every language feature in the same depth or in the same order that you might encounter in a standard programming language introduction. Furthermore, we will explicitly try to take a pragmatic path rather than belabor esoteric details. Those details can be quite interesting and will be there for you when you want to seek them out, but often they get in the way of learning. We will sometimes do things in a less than perfect way as the trade-off is worth the expositional benefit. Overall the goal is to get you to a state of productivity as quickly as possible. Along the way we will provide pointers to further material if you want to go deeper. Setting expectations based on your background A great deal of terminology in the programming language space is built on a base set of shared ideas that have become so entrenched as to be overlooked by most every day developers. There are some lines we have to draw where we lean on some assumed prior knowledge. Thus, if you have never written any code before this might be a challenging book to make it entirely through. If you are willing to take some leaps of faith then you should be able to make it. First and foremost, absolutely zero Rust background is assumed and every new concept will be explained as it arises. If you have a background that only includes garbage collected, dynamically typed languages, such as Python, JavaScript, Ruby, PHP, then the biggest hurdle will probably be working with the type system. That being said, you might just find a wondrous joy associated with the tight feedback loop of a compiler telling you all the things you have done wrong. Moreover, the type inference will let you forget about it most of the time. Some of the points around memory safety might seem less exciting to you because all of these languages are also memory safe. The approach
📄 Page
17
Introduction 11 to achieving memory safety is different but the end result is the same. Some topics around pointers and references might seem new, but all of these languages leak those concepts and you probably already understand them just in a different form. For example, if you write the following Python: def someFunc(items = []): items.append(1) return items a = someFunc() b = someFunc() a.append(2) print(a) print(b) youwill see [1, 1, 2] printed twice. As a diligent Python programmer you know not to use lists as default arguments to functions like this and the reason has to do with values versus references. So even if you don’t explicitly think that you are working with pointers, you definitely do use them all the time. Rust has many high level syntactic features that make it feel surprisingly similar to a dynamic language. If you have a background in functional programming coming from Haskell or the ML family then a lot will feel quite at home. But, the use of mutability and explicit imperative style might be less of your thing. Rust has great functional programming facilities and the type system borrows a lot from these languages. However, Rust is more geared towards giving you a lot of the same safety benefits of functional programming while still writing imperative code. Shared, mutable state is the root of all evil. Functional programming attacks that problem by doing away with mutability. Rust attacks it by doing away with sharing. If you are coming from C++ then you are in for an easier road in some ways and a much harder one in others. I focus here on C++ as it is more superficially similar, but many points also apply to C. Much of the syntax and most of the concepts will be familiar to you. However, there are a few new concepts, like lifetimes, and a few things that look the same but are not, like references and move semantics.
📄 Page
18
Introduction 12 There are APIs you will find in Rust which you might find to be highly performant, but laughably dangerous if ported to C++. You are correct. However, the borrow checker can make such APIs safe. For example, would you give a reference to a stack allocated piece of data to another thread? Would you store a reference to part of a string in a heap allocated struct? Both those are invitations to disaster in C++. They are trivial to do correctly in Rust thanks to the borrow checker and can be great for performance. The API you might find normal in C++ may not be expressible in safe Rust. That is, the borrow checker may not allow you to compile code you consider correct. You may in fact be correct. However, this is usually an API which is easy to misuse and is only correct with a significant amount of cognitive burden on you. Hence, coming from C++ might require the most shift in how you think about structuring programs. You are more likely to “fight the borrow checker” because some of the ways Rust wants you to do things are just plain against your instincts. Rust has a bit of notoriety for having a steep learning curve, but it is actually mostly about unlearning things from other languages. Therefore, having less experience can work in your favor. Getting your environment setup This book assumes an installed version of Rust and some associated tooling. The first step in getting setup is to visit the official installation website: https://www.rust-lang.org/tools/install You should be able to follow the instructions to get setup via rustup. Rust has a fast release cadence for a programming language with a new version every six week. This means that the particular version as of this writing will be stale by the time you are reading it. However, Rust also puts a strong emphasis on backwards compatibility. Thus, as long as you are using a version of Rust at least as new as when this was written, everything should still work for you. Rust 1.37.0 should be new enough for all the code in this book. Moreover, we are using the 2018 edition exclusively. There is an entire guide⁶ dedicated to explaining the editions so we will not cover it in depth here. ⁶https://doc.rust-lang.org/edition-guide/index.html
📄 Page
19
Introduction 13 Rustup The rustup⁷ tool is your one stop shop for managing multiple versions of the Rust compiler on your machine. You can have different versions of the compiler installed next to each other and easily switch back and forth between them. You can install nightly releases to try out new features and then easily switch back to stable for other projects. If you have ever dealt with the absolute madness associated with managing different versions of some languages then you will be delighted at how well rustup just works. One note, for some reason all of the details of rustup can be found in the Github readme⁸ for the project. It is pretty easy to use but the command line help frequently fails me. Cargo rustc is the Rust compiler, and you can invoke it directly, however you will find this rarely to be necessary as the majority of your time will be spent interacting with Cargo. Cargo is a dependency manager and a build system. You use a manifest to specify details of your code and its dependencies and you can then instruct Cargo to build your code and it will take care of the rest. You can have Cargo manage building for other platforms and for quickly type checking via cargo check. You use it to run tests via cargo test and for countless other tasks. We will cover code structure later on, but the primary unit is known as a crate. You can depend on other crates and the public repository can be found at crates.io⁹. This is related to Cargo in that there is quite a bit of default work builtin to Cargo for working with crates.io, but it is not absolutely required. Cargo has its own guide¹⁰ which is a great source of information when you find yourself wondering how to do something with Cargo. You can also always run cargo help to answer your questions from the command line. ⁷https://rustup.rs ⁸https://github.com/rust-lang/rustup ⁹https://crates.io/ ¹⁰https://doc.rust-lang.org/cargo/guide/
📄 Page
20
Introduction 14 IDEs, RLS, Editors The editor support story is getting better and is significantly better than it used to be. A project known as the Rust Language Server¹¹(RLS) is designed to provide the backend for any editor to interact with the compiler and a tool called Racer¹² which provides faster (but less precise) information than the compiler can. This is a project that conforms to the Language Server Protocol¹³(LSP) so that every editor which can act as a LSP client can work with RLS. There is a reference implementation of an RLS specific frontend for Visual Studio Code, so if you are unsure where to start that might be one to try out. If you have a favorite editor already, like Vim or Emacs, then there are plugins you can use to make working with Rust more comfortable. Personally, I use Vim and a shell for running commands directly with Cargo. This is mostly so that I can move between environments with minimal change to my workflow, and I have found that Rust is amenable to this style. There are some languages which are very hard to work with without autocomplete and Rust has not been like that for me. Check out the official website¹⁴ for an up to date list of tools. Clippy The linter is affectionately named Clippy¹⁵. Cargo supports an awesome feature where you can install subcommands via rustup so that you can selectively add components to Cargo based on your needs. Clippy can be installed this way by running: rustup component add clippy and then run with: ¹¹https://github.com/rust-lang/rls ¹²https://github.com/racer-rust/racer ¹³https://langserver.org/ ¹⁴https://www.rust-lang.org/tools ¹⁵https://github.com/rust-lang/rust-clippy
The above is a preview of the first 20 pages. Register to read the complete e-book.