 So, how was everyone today after lunch? How you feel? Cool. So, I might run a slightly faster, but the slides will be available online. And for that matter, we should start. So, let me start you with a short story. Roughly 400 years ago, there was this guy. Avedi Zildian. Not sure if you heard about him. He tried to turn base metals into gold. 400 years ago, they think they'd do that. So, guess what? He didn't succeed. But he found an alloy that makes nice sounds. And he founded a company that still exists. Like one of the oldest companies in the world. So, let's take a similar journey and try to turn something into rust. We're not going to pick a base language, pick a base metal. We'll pick a base language. We're going to talk about C, mainly. So, there might be some reasons for... Am I missing a slide? No, okay. There might be some reasons for rewriting, right? There might be good reasons. For example, you want more performance. You want more safety. You need to start from scratch anyway. So, maybe you want to try a new language. Or you want to try to learn something new for yourself. There's actually one hidden good reason as well. But it applies only if you want to rewrite stuff in rust. And that's if you don't want to use gold, right? Of course, there are some bad reasons that you... You might think about yourself. We want to rewrite it because the new language is cool. Everyone uses it. Or, like, why not, right? Or some guy told you to. So, I'm going to be talking about using rust in LibVert, which is the same project Michael was talking about. But it's not going to be about LibVert that much. So, I kind of assume you know what is rust. I don't think you will go to Wikipedia now, like Google something, come up with this, look there and see... Yeah, rust is the most familiar example of corrosion, but that's not what we're talking about here. Just to make it clear. So, the main question, should you rewrite your project in any language? No. Just no. There might be some reason for that, but don't just go and try rewriting stuff. It's all about consideration. Should you consider rewriting something? Sure, why not? Probably there are reasons why not to do that. Like, if you can't think of any reason why to do the rewrite, then just, of course, don't even consider it, right? This is all going to be about the consideration, whether to do that. There's going to be difficulties. There are difficulties, there will be difficulties always, right? So, it's about evaluating the risk, trying to see whether it's worth it or not. Just to let you know, my goal here is not to give you an answer, whether you should or should not rewrite something in another language. My goal is also not to teach you rust here, right? My goal is to provide you with some information that might or might not be valuable to you if you want to weigh the risks and so on. So, a little bit of my backstory. Like, I started with rust because I heard about it. I'm trying to keep up with new things. I wanted to learn new things. So, I heard about the language and I said, well, what should I try? I had no idea what to try. What I wrote was a sleep sort implementation in Rust and because what I heard rustations like to name things based on their relation to Rust, so I named it oxidation sort. And how did we get to consider a rewrite in Libford? Well, one of our maintainers sent a proposal that, look, we have this project that's 12 years old, how to make it live for 12 more years, right? There were a couple of things what we should do, what might be good ideas. And one of the things was, well, maybe we should adopt a safe language. He actually said go, which I did not like that much. But based on what I did, it was just a sleep sort. You need to handle threads. You need to handle moving board checker and so on. I liked it so much that I said, well, why aren't we going to use Rust instead? And he said, well, it's all about discussion, right? We should, for any language we will consider, we should do a proof of concept and so on. So I said, cool. Well, it's a milestone five years from now, so sure, we can talk about other languages as well. So then the call for papers for DevCon came. I was like, I did not write any Rust. I want to, but I have no use for it now. Let's submit a talk. Maybe it will make me write more Rust. Cool. So some of the problems we were facing when we were trying to fix are things like data integrity, right? Here you have a function where MacMap add that works on an object, you log the object, and you call the same function with locked in the name. You kind of know which function accepts the locked object and which function accepts the object that's not locked, right? Until you get to the other part where it says, well, there's a precondition. It should be locked, right? So there are things like this that can occur. Of course, Rust will not help you with everything, but every little thing helps, right? So yeah, you can partially solve this in C. Like you can have a lock function that returns a different type of object or a different type for the same object and so on, but it's not with the same elegance as some other languages would do that. One of the other things are ownership, right? You have two functions which we have, and this is like a copy paste from our code. I did nothing about this. So one of the functions gives you back the data that we have in the buffer. There's a verb buffer in libvert, and the context is only valid until the next operation on buffer. And we have another function that tells you, oh, the caller now owns a string and should free it when it's no longer required, right? It's in comments. How do you know, right? So yeah, you can kind of see it here from the declaration that one returns const, char pointer, the other one returns char pointer. So you could argue you can understand that, right? Well, unless you get to this, where you have function that accepts const char pointer, const pointer, and because it's not C++, it's C, it will not automatically add const everywhere, so you need to just cast it from various places, right? So you don't really know who's the owner in C. Then you have stuff like optional values, right? Where you have some unsigned long, long, which needs to be unsigned long, long because you need to capture the whole range. So you add a bool, whether it's specified or not. Or you have a pointer, which can be now, some cannot in this structure. Or you have an integer for which unspecified means minus one, right? And for some of these things, you need a function that creates a new structure for you because zeros are not the default, right? So a function that fills it with some defaults. And you have to call this function to get a structure. Of course, in C, the compiler will not tell you, you have to do that. And some other stuff, of course. Everyone wants to get more contributors. Everyone wants to get more maintainers. Ideally, we, of course, want more safety because it's a Libertas project that's used everywhere. It's maintaining virtual machines and doing migrations on top of them. So stuff should not go bad that much. I want to use some object-oriented goodies. We have object implementation like most bigger C projects do. So how do we just start? We want to rewrite something bigger. You can start bottom up. You start writing utility functions. You can start top-down where you write the binary and you just call the C code and you gradually go down. Or you can start from the middle if you have some isolated part of the code that can be written in almost any language like a module. Or you have drivers. We have drivers and backends in Libertas as well. And there's a question whether to write something that looks more like Rust or whether it's C-equivalent Rust. So I figured, well, let's look at the call graph, right? Generate a call graph. Look at the isolated part. So I took hours and hours to get generated. I opened it in Firefox and this is how it looks like. Right? So I did some optimizations. I worked a few more hours on top of that and it looked like this when it worked. It's all red. Because red means there's too many nodes coming from this node. I will not render them anymore. Right? So I made some lists of things to try. Right? I figured out I should start here. I tried a few things and I got to a point where something was working cool. So I looked at the list and it was not even the first thing on the list. So there were small steps. But I already learned things. So there are some tools. There are some things that can help you with rewriting stuff in Rust. There's C to Rust and there's Corout. Those things take C code and give you Rust code that does the same thing. For C to Rust, there's Web interface and command line tool. Corout is the CLI tool. It's actually interesting because it's written in literate Haskell. More languages, right? But this is not the aim of the talk. I wanted to try right Rust. This would take the only thing that I wanted to do from me. Other tool is Rust bind-gen that generates bindings for your C code so that you can use it from Rust. It works weirdly. You need it, but it works in a weird way. It takes kind of long. It needs C-lang because it uses lib-c-lang to parse the code into AST and so on and so on. And it depends whether you use it from Makefile or from build-rs in Rust. In which case all the prerequisites that the bind-gen has you will be building them on the machine where you're building lib-word and they will be your dependencies, right? We will get to that. C-bind-gen, the other way around, it generates C header files or C header files, yeah, for a Rust code. It just works. It's kind of better. Cargo vendor is one good thing. It basically vendors or Rust dependencies in your source tree so that when you're building somewhere from your disk targetball, you don't need a network, for example. But it all comes down to who controls your build system. If it's a bigger project, you want to start with the thing you have, just plug stuff in. If it's a smaller project, you still probably want to do that. The only way you can just move to cargo is if you have superdining project that you can just rewrite in one afternoon, right? That's not what we were talking about. One more thing, if you want something on top of cargo or Rust he doesn't provide, you'll need something else as well, right? Like libraries. If you want libraries and output, Rust gives you a bunch of things that you can choose. But no lib2 library, no package config file, stuff like that. I'm going to skip those. You can ask me later if one more. But it's not that relevant. What's good to have is tests. We have a lot of tests. We have unit tests. We have integration tests. It will help you move on with your code. So what I wanted to do was make the basic thing work in Travis that we have. With Travis, we're running this check. So we need to make this check work. So we need to make this work. So, yeah. Things that are really related to Rust, right? So one of the things is how do you pass information from auto tools to cargo? You can generate some files from .in files. You don't really want to do that. You want to have the Rust part of your project easily buildable. You want to go there and do cargo build or something. And ideally for that you do not you don't want to force some people to run configures just so they can try the Rust part. So you can have cargo features that will be run by make for example those that require some C code but they will not they will not be needed when you just want to build a Rust part, right? So one of the ideas I had and what worked the best was for me just in make file pass some environment variables to cargo build and use them from .info and have same defaults if they are not passed. We will see that. So what did we learn? How to do the rewrite? You like some language so you wait your person cons and then you work build stuff, build scripts. You might be thinking wow, that's fun. No. So let's do some Rust instead. I'm going to need to speed up a little bit I guess. First thing I want to try is to have a function that does not return anything and does not take any parameters. This is how it looks in C header file. Basically what you need to know is you need to have no mangle so that the name is the same in the library. It needs to be public and it needs to have the same size. It just uses the system call method. There are a bunch of others for windows and so on and so on. Second try I tried we have a function that just creates a file with some permissions. So I wrote ver file touch. It's pretty easy. Don't look at don't take my Rust code as Rust. I just wanted to try stuff out. It kind of works. But you can see there's no unsafe. How can you interface with C and have no unsafe? Well, as you can see there's utils C string. Give me a C string from the file name. So I had some utils great that I created and C string basically checks for if it's not null so I made the unsafe part as small as possible and whenever you need unsafe when you're interfacing with another language but what's really good whenever there's a reason for you to use unsafe there are the list of things that you need to make sure basically why you need to use unsafe what the compiler cannot do for you. So a couple of things for the cster from PTR there's no guarantee of the validity of PTR. Well, we kind of assume that someone gave us a valid pointer. It's our code so we can assume that. The return lifetime is not guaranteed to be actual lifetime of PTR. Well, we don't really care because the function does not store it anywhere. There's no guarantee that memory pointed to PTR contains a valid null terminated by it. Well, it's C string. It does have that. We're passing it from C and we used it as a string before in C. So it works. And the last thing, again, it's not being stored anywhere. So finally I wrote a function that actually also returns something. This is what you need to write to get cons car pointer points, strings, cons data meter and so on. And there's a question. Do you do more Rust stuff or more C stuff? Right? This is a... Well, let's work with it like it's a C code. This looks more like a C code. You can do that. You're using bunch of unsafes. You don't really... It doesn't look like Rust. The other thing you can do write kind of Rust code and then around it, like transform the C structures and C stuff to Rust and then transform it back. The problem with this is that whenever you create a Rust structure from the C data, Rust knows about it, it wants to free it. You need to tell it, just forget about this memory. If you don't like Rust, forget about it. So you don't get double free. So... So one of the things is I learned that by tests, right? And the easiest thing to do, what you can do is test your FFI code in C. You can show you the test I have for this function in Rust. You have to create C structures and everything, why, when you can just have a C file that tests that. One of the other things to know about, you need to use the same allocator in the Rust code as you use in C. Of course, if you want to unlock something here and free it here, you can use the same allocator. You can do that if you have nightly Rust. Check some, get some alloc errors. There are two types of errors. The allocation fails or the parameters are not cool. Again, you can use PanicHook and AllocErrorHook and just have function that returns safely. You don't want your program to crash if it's multi-threaded and this does something else on the other thread, right? Again, you need to have nightly for that. That's the build stuff we talked about. I'm just going to run through it. This is how it looks if you want to use bindGaN from makeFile and it still does not properly work. This is how it looks in buildRS where it actually works better. There are some bugs why bindGaN doesn't work properly in make, but you're supposed to use it from buildRS but you cannot use it if you don't have it as a build dependency from your dist. See, bindGaN is kind of easier. This is how you use it from make and this is how you use it in buildRS and it works with dist, dist check, vpath builds and so on. This is what you need for cargo vendor. Just make sure you make the path writable based on automake documentation and use relative data to make the path because, well, some projects did not do that and I don't know how they're handling it. From what I did, I have a list of things to do. I want to use workspaces for different things. That's cool that it works. I have some stuff in separate crates, whatever is used from buildRS. The rust stuff is almost completely optional. That's good because you don't want to just change the dependencies with this big of a thing. There are some more things I still need to do that don't really work properly but we're on our way. Some of the hints that I have for you are all blindly for some stuff you found online. I did not do that and after I looked through all the things that I found they just I'm looking at it and I'm like, how could that work? How? And that includes this one. Maybe I have something there that does not work for you. Use the advantage and learn stuff. That's what we usually are good at and want to do. Also, read stuff. Don't do everything yourself. Read it but just don't follow it blindly. You can use the opportunity to get used to the language coding style so don't adapt the coding style of the language to your stuff that you use in your C code or whatever. As I say, you want to build for a few versions without Rust as a dependency like Firefox did for a couple of versions they said you can build without Rust if you want and after a couple of years they said I don't know, not years. A couple of versions they said well, now you need Rust. Read stuff. I'll leave you to it. We can learn something by corroding the code. It's going to be adventure. There are going to be pitfalls. There are going to be caveats. Even if the rewrite is not successful we can learn something new. We can find out something new just like Avidis did as I said in the first slide of the presentation. Thank you.