Summarized using AI

Building native Ruby extensions in Rust

Guilherme Carreiro • September 11, 2024 • Sarajevo, Bosnia and Herzegovina • Talk

Summary: Building Native Ruby Extensions in Rust

In this talk presented by Guilherme Carreiro at EuRuKo 2024, the focus is on utilizing Rust to create native extensions for Ruby, which traditionally relied on C. The speaker shares his personal journey at Shopify, emphasizing the increasing accessibility of Rust for Ruby developers and the benefits of this dual-language approach.

Key Points Discussed:

  • Personal Background: Guilherme introduces himself as a developer from Brazil now working in Europe, highlighting his journey from learning programming to his current role at Shopify, where he specializes in developer tooling for Ruby.
  • Need for Native Extensions: The speaker discusses the common misconception that native extensions must always enhance performance. He emphasizes the importance of prototyping and understanding the overhead introduced by the foreign function interface (FFI) when integrating Rust with Ruby.
  • Prototyping a Ruby Gem with Rust: The presentation featured a live coding session where Guilherme builds a Ruby gem utilizing Rust's advantages. He showcases the steps involved, including setting up the project structure and defining the necessary files like Cargo.toml, extconf.rb, and the Rust source code.
  • Performance Benchmarking: After developing the native extension, Guilherme benchmarks its performance against existing parsers for YAML (using THMO files) and finds substantial improvements, demonstrating that the Rust-based parser outperformed its Ruby counterparts significantly.
  • Error Handling and Memory Management: The presentation highlights critical areas such as robust error handling and memory management best practices needed when developing Rust extensions. Several strategies are proposed to avoid memory leaks and ensure smooth execution without runtime errors.
  • Production Readiness: To put the gem into production, Guilherme discusses the necessity of precompiling for different platforms to enhance the user installation experience, explaining how to automate this process using GitHub Actions.
  • Takeaway: The key conclusion of the talk is the potential of using Rust for computationally intensive tasks in Ruby applications. He advocates for leveraging existing libraries and gems rather than reinventing the wheel.

Building native Ruby extensions in Rust
Guilherme Carreiro • Sarajevo, Bosnia and Herzegovina • Talk

Date: September 11, 2024
Published: January 13, 2025
Announced: unknown

2024 is such a great time to be a Ruby developer! The language is improving with initiatives like YJIT, enhanced GC, and Prism.

Even when we face language boundaries, we no longer need to default to C. Extending our gems using the modern and fast tooling provided by the Rust ecosystem is easier than ever.

In this talk, I will share my journey at Shopify in creating a Ruby gem with a Rust native extension, discuss the advantages, challenges, good practices, and how to avoid common pitfalls.

EuRuKo 2024

00:00:08.200 thanks everyone thank you um so building Ruby extensions in Rust I'm going to
00:00:14.200 talk about the Journey of building it so I will kind of cherry pey the most challenge uh things that I encountered
00:00:20.880 when I was building this and um yeah I hope by the end of this talk um you can build one yourselves but uh before that
00:00:28.920 who am I my name is G cero I'm actually from Brazil I was born in a place called
00:00:35.120 new Europe in Brazil and uh and now I live in the Old Europe and uh everyone
00:00:41.960 knows about madri so I don't need to talk about this but about new Europe one important thing that we have that is
00:00:47.800 that we have the largest shopping mall in Latin America so this is our special
00:00:52.840 thing and one special thing about this shopping mall is that my mother bought my first programming book at this mall
00:00:59.320 so when I was at teenager was learning about logic and um I started writing programs in the beginning was started
00:01:05.479 with macrom media fles and naturing script and dely and building very silly
00:01:11.119 programs and but that then I I learned that I really L this program and that was my beginning now 20 years later I'm
00:01:19.720 working on Shopify uh I work with developer tooling uh more focus into the
00:01:25.960 liquid developer tooling so when folks install when folks are are building shop five thems and they have code completion
00:01:33.320 is because of the work that my team is doing to the language server the gramar the coding highlighting all the tooling
00:01:40.600 around liquid uh this is the thing that we're responsible for it's a and there's a lot of stuff that involve liquid right
00:01:46.159 because you need to do it yourself when you're developing and we also need to render so this is a bit of my work as
00:01:52.759 well when folks they open a shop and that page renders all the that the parsing work um uh that happens into
00:02:00.840 into that site I I'm more I'm working to that side as well so this is a little bit um where my where is my work um and
00:02:10.679 of course render storefront is a performance critical thing and during developer tooling is also a performance
00:02:16.519 performance critical thing like when you're writing your code and the code completion doesn't load it's it's a bit
00:02:22.720 frustrating we have as a metric that we have 100 milliseconds to show some clue
00:02:27.840 for developers so they are writing a object and they type a DOT we have 100
00:02:32.920 milliseconds to realize the properties available there so we need to do some typing fance a lot of stuff happens into
00:02:39.239 this process uh so it's developer to is also a performance uh critical thing and
00:02:45.480 and that's a bit what I do and um by working to this project uh I had the opportunity of building a native
00:02:51.640 extension not because I wanted in the beginning but because I need it and um
00:02:57.040 when we think about Native extensions in Ruby the first thing that we think is okay
00:03:02.800 I'm going to need to do this because of performance it's going to be faster so rubies is low I cannot use rubby and I
00:03:09.360 believe that's really debatable um here we have an example of some computational thing happening in pure Ruby and let's
00:03:17.519 say that this computational piece here is critical for your business it's
00:03:22.959 critical for your company you you really need to make it this fast and then you say I'm going to extract this native
00:03:28.920 extension rust it's going to be faster and then you you learn how that thing works get excited and you say you know
00:03:36.080 what I'm going to extract everything and then we have this three versions of the same code here but look at this you can
00:03:42.360 think of which one is faster and for sure the third version here is the F the
00:03:48.120 fastest one however the interesting thing is that the second fastest one is
00:03:53.480 the pure Ruby version and the second one is the lowest one so in the second one
00:03:59.159 we introduced all all problems and complexities that bringing a native extension um brings to
00:04:07.120 your project we're bringing all of that you you're not having any benefits from from this so this is the first thing
00:04:14.239 that I would advise if you really think that a native extension can solve a performance bottleneck in your
00:04:20.959 application first of all there's other Alternatives and second prototype because during this prototype you can
00:04:27.360 realize if it can really solve it because of course native extensions they introduce that overhead of having your
00:04:35.280 Ruby program here and facing the ffi boundary to reach the the rust land so
00:04:42.880 we're always have a everything has a price here so that's the importance of the Prototype and that's the thing that
00:04:50.039 we're going to do today we're going to build the native extension together we're going to prototype it and uh one
00:04:55.840 interesting thing about Ruby is that we use a lot of part UMO files right we do
00:05:02.880 use this for database configuration use this for our lters we use UMO files all
00:05:08.160 the time and um I because of this we can guess that Ruby has a very strong and
00:05:14.520 good yo parser to be able to read that file get the properties and configure
00:05:19.600 your project so I have this hypothesis that Ruby has a very good yo parser and
00:05:25.400 I also have the hypothesis that rust has the best thmo parser because all
00:05:30.479 configuration files in Rust they are by convention uh they are tho files so my
00:05:37.160 guess is that rust has the best tho parser that we can find so with this in
00:05:43.440 mind what if today we build a native extension um that uh that's a ruby gen
00:05:50.360 that you can install in your project through part your tho files and and let's make that gen really really fast
00:05:56.600 and uh the first thing that I needed to do to validate my idea is double check if if other folks are not doing that
00:06:02.919 already right so the thing that I did was let me double check the most used uh
00:06:08.360 thmo parsers that we have and double check if they are already using this uh this architectural approach of parsing
00:06:15.479 uh the Tho file using a native extension the good thing is that they are all open source so I could could double check the
00:06:22.080 code base there and confirm they are not using that approach so that means that I
00:06:27.680 can be I can build a Tumo parser gen that's really fast so the first thing
00:06:33.880 that we're going to do is this we're going to create a gen just like we are used to do it but we're going to use
00:06:39.639 this x flag and pass rust as a parameter this is quite similar to the
00:06:46.080 same process that we follow to create a standard G like we're doing here and uh in a moment we're going to
00:06:52.919 notice that the difference are only these three files there um what are this
00:06:59.919 is three last files the cargo tho the XT com and the Li and the LI fire and the
00:07:06.080 lib fire and uh what's special about these three files that are not included
00:07:12.360 on standard Ruby gens cargo. tho is the is the J file for rust projects so it's
00:07:18.759 pretty much this you don't need to think a lot about this one the XTC is a file
00:07:24.919 that all Native extensions being written in C or being written in Rust need to
00:07:30.560 have this XTC file is a file that creates a very specific reip when you
00:07:37.319 install when the users of your gen install it into their machines and during the installation
00:07:43.720 that file gets executed and for example if they are uh installing your gen on Windows this file is going to have all
00:07:51.919 configurations and complexities and dependencies that we need to install to be able to build this native extension
00:07:58.120 from Source on Windows the same for Linux the same for MACC um this file is a reip that describes how
00:08:06.080 how users of your gen can build it from scratch and um the last one is only the
00:08:11.720 source code of your of our rust um gen so if we open this boiler plate here um
00:08:19.639 as you may notice we have this very simple init function here um the only
00:08:25.800 the the very simple thing that this thing is doing this border code is doing
00:08:31.440 is I I have a thiso module here and I'm going to define a single to Method into
00:08:39.320 this tho module um that's called hello and when that gets called the function that's executed is this hello function
00:08:46.320 here and in this function uh returns a string that interpolates this this
00:08:52.040 subject so this is quite simple and um that's pretty much this that this this
00:08:58.399 scul F have and if we open the um the entry point of our gen we don't have
00:09:05.360 anything there there's no code there's one interesting thing though in the fact
00:09:11.480 that this doesn't have any code um it lives in the fact that we have this require into the
00:09:18.120 line in the line five here we have this require fast TMO which the name of our G
00:09:24.200 Fasto SL Fest TMO if we investigate our code base this file doesn't exist
00:09:31.800 there's no code in the entry point of my gem so how this works that's a question that we can ask ourselves so let's try
00:09:38.640 to execute this and if we try to execute this there reget uh that file
00:09:44.480 doesn't exist so it seems like this gen doesn't work so that's the second thing
00:09:49.720 that we need to do after we create our gen we need to Bund install to download the dependencies always always do and we
00:09:57.480 need to compile our native extension so that rust code is going to be converted to something called shared object that
00:10:04.360 we're going to understand a little bit later and then the Ruby code you be able
00:10:09.600 to call that that code that is written in Rust and for Ruby it doesn't matter
00:10:14.760 if you writing this code in rust or in C it really doesn't matter it's the same
00:10:20.320 infrastructure and uh as you may notice here we can all call our method and it
00:10:26.240 just works uh so this was the important step we needed to compile uh our our gen
00:10:32.399 and then we got this fast tho. bundle file this file here it's going to have
00:10:37.959 different extensions depending of the platform you're using if you're using Linux it's going to build a fast tho.
00:10:43.560 SEO uh file if you use Windows it's going to be another one so depending of the platform uh the bundle that's that's
00:10:50.839 build it is different and they call this shared object so this is how the things Works they work end to end you can
00:10:57.839 create your AG you file it and then you can execute but everything looks very
00:11:03.760 magical and we don't like this right we like to understand how things are really working the the reason that's very easy
00:11:11.079 to do this is that there's a lot of Library there's not a lot of libraries but there's some libraries that are helping us uh so let's ignore those
00:11:18.079 libraries let's let's pause this prototype for a minute and let's understand how Native extensions uh they
00:11:24.639 work and this is the most important slide of the talk um here here we have a
00:11:30.160 ruby program in invoking invoking a native extension and we have no libraries involved and um and here that
00:11:38.399 there's no magic there it's pure Ruby uh running but it's difficult to digest so
00:11:44.959 let's digest together the first thing we we want to do uh is to execute discorde
00:11:52.000 uh we want to call Ruby and we want to execute this spots hello from rest if we
00:11:58.480 execute this we're going to get an error this hello from rust variable or method it doesn't
00:12:04.320 exist so let's do the second thing let's require something that's
00:12:10.279 this simplest rust extension and the simplest rust extension is the thing
00:12:16.839 that's going to have this method that doesn't exist and how we're going to put
00:12:22.399 this method inside of this thing that we're requiring right then we have the
00:12:27.480 second line here um what we're doing here we're taking a
00:12:33.240 rust source and compiling it to a bundle that rubby can understands and um and
00:12:40.639 this bundle is compiled in such a way that it it's it's already compiled but
00:12:46.360 can be dynamic linked into your Ruby program so this is the thing that we're doing here we're convert we're taking
00:12:53.199 this simplest rust extension. RS file and we are compiling it into the
00:12:59.800 simplest R extension. bundle and um we compile this using some Flags uh so the
00:13:07.639 bundle can be dynamically linked into a ruby program so this is the thing that we're doing and what we have inside of
00:13:14.480 this simplest extension. RS we have one important thing here we
00:13:21.720 have a function uh that starts with this init uh word here this is a convention
00:13:28.000 when it comes for native extensions when we're building native extensions again being in C or being in Rust uh we need
00:13:35.000 to follow this naming convention in it the name of your module uh and when you
00:13:40.399 follow this convention the ru VM when it needs to require that file it knows that
00:13:46.480 this function is going to be executed so when this file gets required in into the
00:13:53.440 Ruby runtime this RB defined Global function is executed so we Define a
00:14:01.120 global function that's called hello from rest and this this Global function here
00:14:07.199 and then is going to call this hello from rust function it's a rust function that returns a ruby string so it's
00:14:14.720 pretty much this and in then we just need to say to to uh to rust please
00:14:20.199 trust me that RB defined Global function and RB St new these two uh these two
00:14:27.000 functions here they're going to be available in run time so while you're compil compiling it you can just trust
00:14:33.519 that in run time these true functions they're going to be available and they are they are available here when we call
00:14:39.800 our Ruby program so this is how things work end to end when you need to have a native extension again being C or being
00:14:47.639 rust it's the same um back to our prototype where why everything is so
00:14:53.959 easy there because we have these two libraries that support us when we write
00:14:59.240 native extensions in Ruby using rust we have Magnus which are the high level
00:15:05.759 bindings what they me that means right it means that when you
00:15:12.040 are you have your Ruby program right at some point your Ruby program invokes a
00:15:17.600 method that lives inside of the Native extension and inside of this native extension we're going to perform a very
00:15:23.920 comp very heavy work there something that's computationally difficult
00:15:29.600 and then we're going to do that job and in the end we need to return the result of that very difficult thing to do maybe
00:15:36.480 a parer for example and then in the end we return the value to the Ruby land to
00:15:42.319 return this to the Ruby land we cannot return the data types that we have in rust or the data types that we have in C
00:15:49.480 we need to instantiate them into Ruby so we return a ruby string and this is the thing that Magnus helps us Magnus uh it
00:15:58.079 it knows the right way of creating Ruby string so when when you need to create a ruby string you just call Magnus and it
00:16:05.000 creates for you so you can be pretty sure uh that you're not going to have memory leaks and stuff like this uh and
00:16:13.480 what rbcs does rbcs has the SE bindings and all L level infrastructure that we
00:16:19.360 need to work so Magnus uses rbcs we as
00:16:24.759 native extension developers should never really need to do it but maybe maybe if you need to do something unsafe then you
00:16:32.279 can call rbcs directly but you shouldn't need to do this everything that you need to do to build a native extension should
00:16:38.920 leave into the magnos side rbcs is something that we know that exists um
00:16:44.399 but it's unsafe it's unsafe to use really need to know what you're doing there so we have this two two libraries
00:16:51.560 the low level and the and the the low level and the high level one so back to
00:16:58.160 our thmo prototype we want to build a tho parser so we want to build something like this a function that receives a
00:17:06.679 string parses it with a something that difficult and then returns a Ruby value
00:17:13.079 so let's let's do this so here we have our example from the beginning our hello world
00:17:18.720 example and uh and then we can just uh
00:17:24.280 tune in this a little bit and let's pay attention to this parse method here
00:17:29.640 the thing that's doing is it's taking that string as a parameter and it's ask
00:17:35.720 is using the rusty library to par it and then we take that value and we just
00:17:41.520 convert that value to to a Ruby value using this true rub value function here
00:17:47.880 so it's simple as this the code that this native extension has it uses existing library to Parts takes the
00:17:55.080 parts results the result and converts to a Ruby value to a data type that Ruby
00:18:00.919 can understand and U now you're probably thinking how will you convert this value
00:18:07.200 that exists only to the rust land to the to Ruby you can do that uh by doing
00:18:12.840 something like this uh you can take that value and visit it and depending of the type uh you're going to do a different
00:18:19.799 thing so you visit that value if you say string you convert to a ruby string it's a it's a hash it's a or a table you
00:18:28.000 convert to a Ruby hash and then you visited this file and in the end you're
00:18:33.039 going to have a data structure completely visited with all data types that Ruby understand so it's pretty much
00:18:39.640 this it's a very short prototype now we can Benchmark and double check it to Benchmark the thing uh the thing that I
00:18:46.600 did was I created this uh thmo input here into my Ruby file and uh I
00:18:52.039 benchmarked to double check comparing my pars time with all other pars solutions
00:18:57.679 that we have for uh for parsing TMO files and the results very good in the end like the fast Tumo the Gen that you
00:19:04.600 just build with two functions uh is the fastest one and the second fast fastest
00:19:09.840 one is almost three times lower and the next one is like uh 16 times is lower so
00:19:15.720 it's a it's a it's it's a visible performance enhancement that we have
00:19:20.799 here also the other benchmarks to uh to compare the parel from different
00:19:26.440 perspective but the thing is it's it's it's a fair Improvement so I want to go
00:19:33.039 ahead I want to take this gen it put in production because let's say maybe you have a business need where you really
00:19:38.919 need to pass the files really really fast when you are doing something or rendering
00:19:45.039 something or doing some developer tooling uh but the thing that you need to do to put this in
00:19:50.559 production uh is to fix some stuff that you left behind the first thing is error
00:19:55.919 handling here in the Prototype code we have this unwrapped thing what this means in in Rust is that
00:20:03.960 we're taking this string we're PR parsing this string and we're saying trust me the parer is going to work just
00:20:10.760 give me the successful result however uh if for example if your input if your
00:20:17.880 string has a syntax error in your tho file then this line here is going to
00:20:23.000 fail as we're going to notice here so we can call the console we can call our J
00:20:28.280 here and if we pass an invalido invalid tho file this is the
00:20:34.480 thing that happens we get this fatal exception fatal exceptions they are no
00:20:39.840 good new our Ruby programs they can stop our execution so this is not a nice
00:20:45.520 thing so the the important thing that we need to do when we are building uh when
00:20:53.200 we're building a rubby Jan with Native extension is that we should have no
00:20:59.200 panics into our program and the way to do this we can just remove this arm call
00:21:05.159 here so instead of doing this and doing what we were doing before we're going to
00:21:10.279 ask to the parer here like thmo please take that value uh from the string and
00:21:18.000 if the result is this okay result we convert this to a ruby type if it's not
00:21:24.000 a okay result then please um create an error for me
00:21:29.760 and uh and here we have another interesting convention that Magnus helps us because this method now no longer
00:21:36.159 only returns a Magnus value it returns a result and this result can be a magnum
00:21:42.799 value or a magnum error into the Ruby land when this methods get executed and
00:21:49.880 the return type is a Magnus error we're going to raise that exception so we we
00:21:56.320 don't have this data type in our Ruby program or we get the result or the
00:22:01.679 error that we are creating here gets raised and here we have Just for future
00:22:07.159 reference this is how we create errors when we are uh when when we are into
00:22:13.400 into the rust land we pretty much take the module off of a stumo and we Define
00:22:20.080 a neep so it's just some PRX code to create an error class uh but cool and uh
00:22:28.360 and and then we have it and if we execute again we can notice that now we get that fast thmo eror that we want so
00:22:36.640 cool Oren is fast Oren handles there as well the next step to double check
00:22:42.520 before but a native extension production is double checking for memory leaks
00:22:48.360 because that's no good in production as well and uh maybe someone would say like
00:22:54.279 oh rust programs they are everyone says like rust is say so you don't need to be
00:23:00.120 concerned about memory leaks when you're writing this kind of program uh but that's not true uh here we have a rust
00:23:07.679 program that has a memory leak and the reason is that we're using this unsafe statement here and then we can probably
00:23:15.480 think okay I can just not use un safe statements into my program and then I
00:23:21.039 will have zero memory leaks into into my into my gen however are your dependence
00:23:28.760 safe as well can be exhausting to check the code base of all dependencies that we have and the dependencies of the
00:23:35.480 dependencies just to be sure that nobody's using unsafe in the truth is sometimes they will be using unsafe in a
00:23:42.679 safe way which interesting uh so the only way to really be sure that you can
00:23:49.840 put this in production is is is measuring and then we can use some tooling to do this we can use V grind
00:23:57.000 and V grind helps us to double check if we have some memory leak into a program
00:24:02.640 so for example we can execute this program here that we have and uh in this program we we can clearly notice into
00:24:10.080 the output that we have four bytes there into the definitely lost section so we
00:24:15.960 do have a memory leak into this program and and this is is no good generally we
00:24:21.760 don't we're not so concerned about memory leaks when we are programming Ruby because in Ruby we have our garbage
00:24:27.559 collector um to collect all the the reference for us and be sure that you don't have leaks but the same is not
00:24:33.919 true when we writing a native extension in C Hunning rust so it's really important to measure this kind of thing
00:24:39.760 and be aware that we need to manage the memory ourselves if we do this into our agend
00:24:47.000 the way that's that's right now um this the output that we're going to get from V it's quite exhaustive we have a lot of
00:24:54.960 stuff happening here but this is not our program this is the Ruby VM running into the program and when you measure to
00:25:01.520 double check the memory we get a lot of noise so it's really difficult um to uh
00:25:07.600 separate what's what's the Ruby VM and what's my progr like it's it's it's
00:25:13.200 difficult to really um understand you still can do it you can do some benchmarks double check how numbers are
00:25:20.279 F waiting but the the approach that I recommend is using Ruby main check with
00:25:27.240 Ruby Main Check he uses V grind under the hoods but it removes a lot of noise so you can um you
00:25:34.640 you can execute your program use rubman check and it's going to give you a clearly signal if you have or if you
00:25:42.520 doesn't have do not have a an issue there also the Ruby main check has a
00:25:48.279 GitHub action so for example you can add this into your pro into your repository
00:25:53.320 and every time that someone opens APR into your gen that thing is going to run
00:25:59.360 and it's going to double check um if you have a memory leak or not so it it's
00:26:04.520 it's really useful into that into that site so cool we created our gen we
00:26:10.440 validate into the Prototype that's the fastest possible implementation so we want to make it production ready we did
00:26:17.919 the error handling we checked the memory and we find from all those perspective so now let's publish so folks can use it
00:26:25.080 however if you publish it uh folks they're going to see this mess for a long time
00:26:30.320 because when they install your gen it's going to invoke that conf xt. RB file
00:26:37.240 that has the reip to compile from source and that takes a long time and then I
00:26:42.480 was thinking how the good J they solve this kind of thing and then I double checked was time and the thing that they
00:26:49.279 do is they precompile everything so if you are uh working for example on Mac
00:26:57.760 you stall was time you you don't need to compile all the things from sources
00:27:03.760 download the dependencies you just download that bundle and it's ready to use so it's really really fast to to
00:27:09.880 install um your J with a native extension when it's PR compiled but this
00:27:16.640 is uncomfortable right because then to publish every time that you publish I'm going to need to run into my machine a
00:27:24.520 pre compilation process for all platforms so this thing that folks generally do is this uh we precompile
00:27:32.559 for the most used platforms and we don't do this locally we have a GitHub action
00:27:37.600 so every time that you cut a release for example create a tag on GitHub this
00:27:43.480 actions stried and it publishes the Gen for you so you don't need to to do it yourself and that's the thing that I did
00:27:49.200 on Fast TMO uh so you can just use this and but the f t it's not a really a real
00:27:56.840 proposal into that sense like oh I feel like everyone parsing theile should use this it's just a proof of concept that
00:28:03.960 shows how we can we can instead of writing everything from scratch we can
00:28:10.000 double check if there's a gen for this or if we have a a rust crate that's already doing this so we can reuse both
00:28:16.519 ecosystems on Ruger you don't need to limit ourself so the first TMO goal here
00:28:22.279 is not to replace any TBO parser is more showing that's viable um and and this
00:28:28.679 repository is open source here so if you want to double check it and get some
00:28:34.159 example or even contribute it to this project you can totally do it uh one uh
00:28:41.080 interesting thing about the Fasto is it's really really fast to pars but when
00:28:46.240 I was doing the same code uh to generate just like we do on on Ruby Ruby we have
00:28:53.080 the json. pars method and we have the json. generate method that generates the
00:28:58.200 string back from from a data type and um when we when I tried to implement the
00:29:03.559 generate it wasn't faster than the other options so it's the best options for parsing but not it's not the best option
00:29:09.840 uh to generate it back so if you want to take this challenge if you want to ex do some exercise and say I want really
00:29:16.159 start experimenting with Rusty EXT extensions you can totally do this uh so
00:29:23.080 this is this again this is the key message like it's I I believe it's really valuable to reuse the stuff that already
00:29:30.080 exist instead of building them for Scratch and frequently more than before now you can
00:29:37.240 check both ecosystems you can check Ruby Jens and check the rust crates if you
00:29:42.600 have if you need to do a very computational intensive task and uh if you have your that option there you can
00:29:48.799 follow the steps it's very straightforward H and and then build that gen that solves your your problem
00:29:55.399 don't need to resolve yourself and Implement from scratch so it's pretty much this the slides are
Explore all talks recorded at EuRuKo 2024
+39