Talks

Rust for Rubyists

Rust for Rubyists

by Steve Klabnik

In this talk titled "Rust for Rubyists" presented by Steve Klabnik at the Rocky Mountain Ruby 2015 conference, the speaker introduces the Rust programming language and its relevance to Ruby developers. Klabnik explores the importance of understanding low-level programming and how Rust can enhance Ruby development by providing performance benefits without the complexities often associated with C.

Key Points Discussed:

  • Introduction to Rust and Ruby: Klabnik expresses his love for both languages and emphasizes the goal of his talk: to teach Ruby developers about the benefits of learning Rust.
  • Abstraction in Programming: A central theme in the talk is the concept of abstraction. Klabnik discusses how programming languages utilize abstractions to manage complexity and the relative categorization of languages (e.g., Ruby as high-level and C as low-level).
  • Performance Issues in Ruby: Ruby is described as slow when compared to other languages, and Klabnik attributes this to implementation decisions that impact performance.
  • Rust's Compiled Nature: Unlike Ruby's interpreted nature, Rust is ahead-of-time compiled, which means the code is compiled into a standalone program. This results in fewer overheads during execution compared to Ruby.
  • Method Resolution: The speaker illustrates how method resolution in Ruby is complex compared to Rust, where types must be explicitly defined, leading to fewer ambiguities and increased safety.
  • Practical Code Comparisons: Klabnik presents comparisons between Ruby and Rust code to showcase differences in syntax, performance, and clarity, emphasizing how Rust's structure allows for better performance and fewer runtime errors.
  • Learning Opportunities: He encourages Ruby developers to embrace Rust as a means to enhance their coding skills and optimize their applications. By understanding Rust, developers can improve the performance of Ruby applications without losing the expressiveness that Ruby offers.
  • Conclusion: Klabnik wraps up by advocating for a culture of exploration among developers, encouraging them to appreciate the unique strengths of different programming languages rather than fostering conflict over language preferences.

Overall, the talk serves as a call to action for Ruby developers to consider integrating Rust into their skill set for better performance and reliability in their applications.

00:00:24.160 Hi everybody, I'm Steve. I am here to talk about both Rust and Ruby, two programming languages that I love.
00:00:31.009 I updated my abstract a little since it was submitted for the program, so this talk will be a bit more high-level.
00:00:37.790 I'm going to provide a brief language tutorial since I have 45 minutes, but it won't strictly be about teaching you an entire programming language in that time.
00:00:48.920 What's important is that I want to teach you why you might want to learn about Rust and how learning Rust can help you improve your Ruby development.
00:00:56.000 So, that's the focus of this talk: what Rust can teach us about Ruby.
00:01:02.300 Let me briefly cover my background.
00:01:07.520 I'm a former Rails contributor, ranking around 35th all-time before I stepped back from that.
00:01:13.130 Now, I work for Mozilla on the Rust programming language as my full-time job.
00:01:18.530 I love it a lot; it's great, and I have read all the documentation.
00:01:25.399 So, when you inevitably check out Rust, if you can't figure it out, I apologize in advance, as that would be my fault.
00:01:36.320 Now, let's talk about abstraction. The fun thing about programming languages, in general, is that they are built on abstractions.
00:01:44.000 We discuss abstractions frequently, considering ways to create new ones and manage existing ones.
00:01:49.820 We often categorize abstractions as good, bad, or leaky. Fundamentally, abstraction underpins programming languages.
00:01:57.979 Interestingly, everything can be framed as an abstraction.
00:02:03.229 We tend to think of abstractions as existing in levels, which leads to phrases like 'Turtles all the way down'.
00:02:08.899 However, abstractions are not strictly hierarchical or tiered.
00:02:14.989 In programming terms, we often refer to Ruby as a high-level language and C as a low-level language.
00:02:21.530 Yet, from the perspective of an electrical engineer, C could be viewed as high-level, while assembly would be low-level.
00:02:26.600 This relativity shapes how we categorize programming languages.
00:02:33.230 For example, Rust can handle low-level tasks like C but offers high-level features akin to Ruby.
00:02:39.209 So, is Rust a low-level or high-level programming language? This is where the abstraction hierarchies become muddled.
00:02:45.630 An enlightening example is that you can develop abstractions in divergent ways.
00:02:52.349 You start from the same premise and can arrive at seemingly incompatible conclusions.
00:02:58.470 In computing, you can think about computation mathematically or through the assembly and machine code.
00:03:05.190 Interestingly, modern processors don’t even run assembly or machine code directly but utilize microcode.
00:03:10.950 This means there’s a programming level beneath assembly, linking to physical transformations in hardware.
00:03:16.049 This chain eventually leads to areas where complexity overwhelms, leaving us uncertain about how things work.
00:03:21.299 Thus, thinking about abstraction reveals its universality.
00:03:28.590 Right now, I'm using an abstraction to convey my thoughts through English.
00:03:33.630 I'm translating ideas in my brain into a structured language, which others then interpret.
00:03:40.680 As we know, this process can encounter bugs, and the output isn't always clear.
00:03:46.650 Everything we do is wrapped in abstraction, or what can be considered a lie.
00:03:53.219 A lesson that resonated with me from college is that the fundamental role of operating systems is to maintain this illusion.
00:03:59.550 For instance, every program on your computer believes it is the only program running.
00:04:05.129 It assumes access to the entire memory space, even when that isn't true.
00:04:11.580 The operating system utilizes virtual memory to manage this illusion, providing the impression of infinite RAM.
00:04:17.639 A funny memory from my childhood: I saw an ad for a program called RAM Doubler that claimed to double your memory.
00:04:24.990 Being a nerdy kid, I was skeptical, knowing it was a virtual memory implementation.
00:04:30.539 This program allowed the Mac OS to mimic virtual memory, which was vital in a time of limited memory.
00:04:37.499 Essentially, every action we undertake is enshrined in this tapestry of lies.
00:04:42.839 One of my favorite sayings is that another term for 'eventually consistent' is 'inconsistent'.
00:04:50.529 Why are these lies acceptable? Ultimately, we can only process so much information at any given time.
00:04:57.919 If I had to consider quantum mechanics while moving my hand, it would be a daunting task.
00:05:03.469 So, we create abstractions that allow us to overlook certain details.
00:05:08.479 It's impossible to consider every detail all the time.
00:05:14.289 What I aim to discuss today is a bit about Rust and Ruby!
00:05:19.959 I'd like to help you develop a more intuitive understanding of Ruby internals.
00:05:27.630 I will present abstractions, leading me to build some delightful lies.
00:05:33.790 This slide is necessary because I gave this talk for the first time last week.
00:05:40.650 I realized that Koichi Sasada, the author of MRI, was in the audience.
00:05:47.930 I felt it necessary to preemptively apologize for potentially misrepresenting his work.
00:05:54.110 Fortunately, Koichi provided valuable feedback, which I've now incorporated.
00:06:00.289 If you're interested in this topic, I highly recommend Pat Shaughnessy's book, 'Ruby Under a Microscope'.
00:06:06.450 It beautifully explains how Ruby works and serves as a great introduction to programming languages.
00:06:13.920 Ruby was designed for programmer happiness; that's what Matz had in mind.
00:06:19.060 To achieve this happiness, some performance details were overlooked.
00:06:25.570 Thus, we find Ruby slow compared to many programming languages.
00:06:30.920 However, we must remember that comparisons of language speeds relate to their implementations, not the language itself.
00:06:37.670 For example, JRuby is considerably faster, showcasing differing Ruby implementations.
00:06:44.680 So why is Ruby perceived as slow?
00:06:51.360 Let's discuss some internal mechanics and decisions that contribute to Ruby's performance.
00:06:58.020 Initially, a Ruby program processes a code input through the interpreter.
00:07:05.889 The first step is tokenization, where the code becomes a sequence of tokens.
00:07:12.840 The parser then checks for valid order and coherence among these tokens.
00:07:18.680 For instance, an invocation like '+ 2 2' is valid in that tokens exist, yet lacks correct ordering.
00:07:25.370 The next stage is compilation; Ruby compiles parsed representations.
00:07:31.760 This compilation can lead to confusion, as Ruby does compile its operations.
00:07:39.380 Ultimately, compiled code runs on MRI, Matsumoto's Ruby interpreter.
00:07:46.090 A significant difference introduced from Ruby 1.8 to 1.9 is this compilation step.
00:07:53.120 Ruby interpreters are limited in their understanding of Ruby; they function as separate virtual machines.
00:07:59.260 The interpreter transforms Ruby code into instructions that the VM utilizes.
00:08:06.200 This process often involves working within Docker containers or cloud VM images.
00:08:12.860 This metaphor of multiple layers of machines is aptly illustrated when considering programming.
00:08:19.150 To better visualize these steps, here's a practical Ruby example.
00:08:24.510 The operation '2 + 2' is valid, and when executed, the code gets tokenized.
00:08:30.580 Tokenization translates '2 + 2' into a representation consisting of two integers and a plus operation.
00:08:37.660 Parsing converts it into an abstract syntax tree (AST), structuring the operation for the interpreter.
00:08:43.910 This AST eventually compiles into MRI bytecode for execution.
00:08:50.150 The interpreter processes each step, running the plus operation in an adorable sequence.
00:08:57.550 Now, let's compare Ruby's internal workings with those of other languages.
00:09:04.670 Most programming languages operate under a similar model of tokenization, parsing, and compilation.
00:09:10.630 However, the distinct ways of representation give rise to unique programming styles.
00:09:17.560 For example, in human languages, certain expressions may be simpler in one language compared to another.
00:09:24.930 German offers compound words describing detailed concepts, which English likes to unpack through long explanations.
00:09:30.559 In Japanese, verbs always come at the end of the sentence, creating entirely different constructs.
00:09:37.629 Thus, across different languages, programming goals often change in response to particular features.
00:09:43.820 The focus of my discussion will revolve around performance comparisons.
00:09:50.000 Let's delve into the intrigue of Rust as a programming language.
00:09:56.150 As a small visual joke, I’ve playfully added 'st' to the top of Ruby.
00:10:04.410 I jokingly mentioned that I'm only programming in languages starting with 'ru'.
00:10:10.530 In the Rust community, we affectionately refer to our crab mascot, Ferris.
00:10:18.440 Rust is an ahead-of-time compiled programming language.
00:10:25.020 This means that rather than interpreting the code each time, you compile it once.
00:10:32.400 With Rust, when you call the Rust compiler on a file, it outputs a standalone program.
00:10:38.470 In contrast to Ruby, where every execution involves tokenizing, parsing, and compilation.
00:10:44.640 There’s an overhead in dynamically interpreted languages that ahead-of-time compilation can circumvent.
00:10:51.510 However, this isn't a strict limitation; other practices can manipulate these models.
00:10:57.820 For example, one could pre-generate the MRI bytecode and load it on demand.
00:11:04.150 Thus, the methodology and culture of a programming language greatly influence how efficiently we can code.
00:11:11.229 Let’s discuss how methods are called in Ruby.
00:11:19.060 The operation 'foo' can be complicated, and for those unfamiliar with Ruby's method resolution, here's a diagram.
00:11:26.300 In this process, we check the current object's method table, searching for our method.
00:11:33.040 If the method isn't found, Ruby checks parent classes, continuing this resolution process.
00:11:39.260 If Ruby fails to identify the method, it’ll invoke 'method missing,' essentially allowing for flexibility in method calls.
00:11:45.860 So, this can lead to unforeseen complexities in understanding method behavior.
00:11:52.320 Time to present an example: a simple Ruby function accepting one argument that returns five.
00:11:58.960 Ruby must navigate its complex method resolution when invoking the function.
00:12:05.530 Next, here's a Rust equivalent to illustrate a stark contrast.
00:12:11.020 The Rust function is clearer, detailing its types and functionality without ambiguity.
00:12:18.520 In Rust, you must define the entry point of your program with the 'main' function.
00:12:24.860 The approach is simpler, with explicit types leading to more stringent control.
00:12:31.410 Rust compiles down to assembly code involving a straightforward sequence of instructions.
00:12:38.529 The assembly structure is often succinct and readily understandable.
00:12:45.210 Much effort in Rust emphasizes low-level programming comprehensibility.
00:12:52.140 Perception of difficulty differs among programmers; what might be simple for some may appear complex to others.
00:12:57.840 Many find assembly code intuitive compared to high-level frameworks like Rails.
00:13:05.780 There are different strengths and weaknesses in programming, affecting preference in language choice.
00:13:12.160 I’m interested in showcasing various Rust and Ruby functionalities.
00:13:18.640 In Ruby, one can instantiate a class, form its attributes, and define a return statement.
00:13:25.680 Rust takes a different approach—favoring structures and implementations.
00:13:30.840 This diverges significantly from Ruby’s object-oriented expectations.
00:13:37.789 In Rust, you define a structure and then implement the behavior separately.
00:13:44.130 Using Rust, this delineation fosters clarity of data and behavior.
00:13:50.580 The compilation yield is more concise, leading to optimized performance.
00:13:56.900 So, expect a multitude of assembly lines from Rust.
00:14:03.300 Optimizations are possible, enhancing generated code performance.
00:14:09.490 Rust’s compiler leverages optimization strategies to enhance execution efficiency.
00:14:16.030 Ruby, conversely, performs limited optimizations as a general rule.
00:14:23.080 Let’s examine how Ruby operates in comparison to Rust.
00:14:28.750 In Ruby, we specify class behavior through modules and mix-ins.
00:14:35.600 So you can build classes to achieve desired functionality through behaviors.
00:14:41.770 In contrast, Rust implements traits and structures distinctly.
00:14:47.900 The separation of attributes and behaviors creates limitations.
00:14:53.210 There is explicit type binding in Rust, which results in rigorous methods.
00:14:59.590 This leads to reduced ambiguity when executing function calls.
00:15:05.620 While flexibility exists in Ruby, it’s crucial to define traits in Rust clearly.
00:15:12.340 By requiring the method to be defined, Rust grants more safety in function calls.
00:15:19.540 So, Rust brings type safety and structure that Ruby may lack.
00:15:26.590 With this rigidity comes better performance, reducing overhead.
00:15:33.420 Let's explore some practical code comparisons next.
00:15:36.900 When we write functions in Rust, they need clear parameter types.
00:15:42.690 Rust captures implementations efficiently while retaining program clarity.
00:15:49.739 Moreover, a brief glance at these structures clarifies programming intentions.
00:15:56.050 Let’s wrap this segment by underscoring the necessity of performance considerations.
00:16:05.340 The message here is that Rust excels at performance where Ruby slows.
00:16:11.790 Ruby, despite its slow nature, remains beloved by many developers.
00:16:18.570 Both languages possess unique advantages; exploring both can enhance your skills.
00:16:25.200 We often misjudge programming languages, causing unnecessary conflict.
00:16:33.459 Embracing the distinctions among languages is vital for new programmers.
00:16:39.569 It's frustrating to see individuals demonstrating disdain towards others’ chosen languages.
00:16:44.970 In crucial ways, every language adds value to the programming landscape.
00:16:51.740 This promotes exploration and trying out new languages whenever possible.
00:16:57.140 I encourage everyone to appreciate the multifaceted nature of coding.
00:17:03.650 Let’s foster a culture of exploration and curiosity, rather than resentment.
00:17:10.130 Ultimately, programming languages ought to unite rather than divide us.
00:17:16.560 Now, I’ll take a moment to present additional Rust functionalities and discuss their merits.
00:17:23.389 Rust shines in the realm of safety and performance, especially in systems programming.
00:17:29.920 Rust helps developers create reliable, efficient applications, reducing runtime errors.
00:17:36.389 Ruby developers can also benefit from understanding Rust's functionalities.
00:17:42.820 This perspective opens the door to reduce bottlenecks while harnessing Ruby's expressiveness.
00:17:50.010 By leveraging Rust, it's possible to add performance to Ruby applications without sacrificing ease of development.
00:17:56.270 Thank you, everyone, for being an engaging audience!
00:18:00.600 I'm happy to take any questions!