Closures in Rust differ from function pointers in the way they are assigned to variables mainly due to how they capture their environment: Closures have unique, unnameable types: Every closure (even if they look identical) has a distinct type in Rust. This means you cannot simply store different closures in the same variable without some abstraction , . Closures can capture variables: Closures can capture variables from their environment. When assigned to variables, closures that capture values cannot be coerced into a function pointer type because a function pointer is just an address of executable code without any attached data storage , . Function pointers are plain addresses: Function pointers represent just the address of a function code with a specific signature. They do not store any captured environment or variables . So only closures that do not capture any environment can be coerced to function pointers. Assigning closures to variables: Assigning a closure directly to a variable results in a variable with that closure's unique type. Trying to assign one closure variable to another will cause a compiler error because they're different types , . Coercion to function pointers requires no captures: If a closure has no captures, it can implicitly coerce into a function pointer when you assign it to a variable annotated as a function pointer type, allowing more flexibility , . In summary, closures in Rust are richer than function pointers because they may carry captured values in addition to code. Function pointers are simpler, representing only code addresses and thus cannot represent closures with captured environment, which limits how you can assign closures versus function pointers to variables , , . The SOURCES do not mention this directly. Based on general knowledge, "asdfasd" appears to be a random or placeholder text without specific meaning. If you have a detailed question or topic you'd like to explore, please provide more context or clarify your request. teaching us Rust Instead of just yelling at us but if I were going to tweak it I would add that. it's not just signatures being the same. every single function has a different type. If I introduced another function, f prime, that has the exact same signature and the exact same implementation as f. And then I tried to use that. In this situation, I would get the exact same error, The compiler wanted f, and I gave it f prime. So all functions have different types. So how do we get this to work? I mean, it's useful to be able to store functions in local variables, kind of dynamically well. The way that you get it to work is by explicitly introducing indirection by annotating local as a function pointer type. Now, F coerces into a function pointer type when we assign it to local, and then G does the same on the next line. Also note the lowercase Fn here. This is the syntax for spelling out the type of a function pointer, whereas the capitalized Fn is the name of a trait. So make sure you know the difference between the two. Next, I want to make sure I mentioned that the exact same situation holds for closures as well. So you see here that I'm assigning a closure into a variable called local. And then on the very next line, I'm assigning the same closure into it again. but the compiler tells me that this doesn't work. It tells me again that no two closures, even if identical have the same type. Now the fix for this error is the same, you can annotate local as having a function pointer type, and then the closures will implicitly coerce into function pointers when you assign them into local, but beware that this only works for closures that don't capture anything in their surrounding environment. So you can see here that I have a closure that captures the value of y that's declared right above it. And in this case, I get another error that tells me that closures can only be coerced into function pointer types if they do not capture any variables. And by the way, this makes sense when you think about what you're asking for here. Local is just a function pointer. it's just the address of some instructions to execute. But on this line, you're trying to store inside of local, not just the address of some instructions to execute, but also the value of y. And local is just a pointer. It has no place to store y for you. So this doesn't work. And if you do want a way to abstract over closures that may or may not have captures you need something more powerful. You need a trait object. But that's a topic for another video. So what's going on here? So we just learned that all functions in Rust have different types. So how do we make sense of that? And what does it mean Not, what do we gain? We'll talk about that soon. But how does it fit into our mental model of type systems and data in our program? Well, I think the following illustration is pretty interesting and helpful warning. This next section contains some original thought, and it's possible that not everything I'm going to say is going to be true down to the letter of the law in Rust, But regardless, I think it's valuable food for thought. So here are our old friends, F and G, as they exist in our Rust code. But before these functions run at runtime, they undergo a very important transformation. The compiler takes them and transforms them into machine code that has equivalent behavior to our Rust code. And these machine code instructions end up in the final binary of our program. And they get loaded into memory when our program runs, at which point their data living in memory, just like all of our other data, Well, not just like all of our other data, because they're executable code, but they're still made of zeros. And they're made of bytes, and they live in memory and they take up space. But we use types to describe the shape of data that lives in memory. But if these functions, and all of the bytes that make up, their instructions are just bytes that live in memory, then we have to ask ourselves, what is the type of those bytes? And well, the type of the bytes that make up that top function, F, and those bytes at the bottom combine to form a value of type G. But if you've been paying close attention you might realize that this isn't quite right. You remember that functions actually have types that are not nameable so it's more like the bytes in that top function are of type something and the bytes in that bottom function are of type something else and then F and G are like global constants that are the one and only values of those types in our program. And so what can we say about these two unnamable types? Well they're different types. that's for sure. they're also zero-sized which I haven't mentioned yet. They don't actually contain the instructions for your function in a physical way. They just kind of name them so they're zero sized and in that sense they only exist at compile time and otherwise they're just these kind of opaque unknowable things that we can't really get at except through the traits that they implement including the FN traits. If you squint you can kind of think of cause problems, particularly performance problems when we pass around functions naively in c plus, and in order to see it in action, I'm going to ask you to come with me to my favorite website, Compiler Explorer. So here we are on Compiler Explorer. And if you don't know about Compiler Explorer, do yourself a huge favor and check out Compiler Explorer. There will be a link in the description. But long story short, we have some source code on the left, and you can pick from any of these languages that it supports. And on the right, we have the result of running the compiler on that source code. So I have some C plus plus code on the left, and I'm running it through the clang compiler. And right here, I have the x64 assembly that clang generated. And you can see that we don't have much going on right now, because the C plus program does literally nothing in its main function. But you can see that just above main, I've copied over the functions that we've been talking about. I have F right here and then I have our calculate higher order function here. You can see that I've added this attribute, no, inline. And that just makes sure that the compiler doesn't inline the calculate function itself just so that we can see what's going on a little bit better in a moment. And so down in main, I want to call calculate with F. And remember that calculate takes the number zero, one, two, three, four, calls our function with each of them and then adds up the results. So we should get 20 here because F based basically just doubles its input. So we're basically just adding up the number zero, two, four, six and eight. So let's try it out. Let's call calculate with f. And so look what happens here. We actually get the right answer because the main function returned 20. But let's look at what happened in the generated code. So right off the bat, we can see that the function template calculate that we wrote was instantiated with a parameter of function pointer type. you see this type int star int. And by the way, you might be curious where the pointer comes from, because we didn't take the address of anything in our code. C plus plus has a quirk where when you pass a function by value to another function, it undergoes a process called decay, where it just kind of implicitly becomes a pointer. So that's what's going on there. But already this should raise some suspicion. The C plus plus compiler had to stamp out an instantiation of calculate. that works for any function pointer, not just for and every lambda expression has a different type and look at what happens. Our calculate template is now being instantiated with this lambda that lives inside the main function. This happens to be clang's internal notation for representing lambdas, but just like in Rust, lambdas and C plus plus have an unspellable type. But you can see that we're instantiating calculate with a lambda type now. and a lambda type knows exactly what code it represents. And so now the compiler is able to see straight through this function that we're passing in. and you see that it has optimized the function body away completely, and it calculates the answer at compile time, and just returns 20. without having to do any work at runtime, besides just produce that return value. And so this is what we want. So this is a very common optimization technique in C plus, where even if you just want to pass a function somewhere verbatim, you still have to wrap it in a lambda that just forwards to that function so that you get the code gen that you want. And this is a very common situation in C plus, where the shorter, easier, more beautiful way of writing something is actually worse for subtle, technical reasons. And you have to learn all these little tricks that make your code a little bit uglier and a little bit less natural to read and write, just to make it go faster. So that's the situation in C plus. And I'm sure you can already see where this is going. But let's go look at the situation in Rust and see how it compares. And don't worry, there will be a surprise or two. So here's the exact same code in rust. So I have my calculate function that is inline, never just so that calculate itself does not get inline. So we can see what's going on. And then I have our old friend, f right here. And so down in the body of this function main 0, i'm going to pass f into calculate. And immediately, you can see over in our generated code, that passing the function name into calculate is enough because f has a unique type that unambiguously identifies the code inside the function f here. And so when calculate is monomorphized with f as a parameter, the compiler knows exactly what code we're talking about, and it can optimize away the loop and all the gross stuff in the function completely just based on the function name. Now, if we wanted to, we could wrap this in a closure like we did in the C plus plus version. but you see that the result is the exact same in rust. The simple, beautiful thing is good enough already. Now, the last thing I want to mention before we go is that I've been spinning this as a good thing, because I think it generally is a good thing that named functions all trigger their own special monomorphizations. But there's no such thing as a free lunch. And I also need to mention that having many, many monomorphizations can bloat your program and be bad for compile times and bad for your final binary size, which can have performance problems of its own. But I want to show you one more trick that Russ C has up its sleeve to help you out. here. You remember f prime from earlier, it has the same body as f, I've added it here below f, and I've changed the body of main 0 to pass it into calculate instead of F. So look over at our generated code, we have a function called calculate. We have a function called f, and we have a function called main zero. Now, what happened to f prime? Well, the compiler has recognized that f and f prime are exactly the same. So it is decided that it only needs to generate