Functions are simple—pass a value, get a value in return (or void in some languages). In Rust, it’s the same, except for one key difference: ownership is also transferred to the function. If the function returns a value, it also gives back ownership.
However, Rust provides flexibility—you can pass a reference and get the reference back, or pass a mutable reference to allow the function to modify the original value.
Let’s quickly go through this and lock it in our minds!

This function simply passes a value to takes_ownership and prints it. As mentioned earlier, it also transfers ownership to the function. So, if you try to print msg after the line takes_ownership(msg);, you’ll get an error because msg is no longer valid in the current scope.
If you wan to return the message you have to do something like below code –

For some this might look too much of work and chaos at the same time, however as mentioned earlier Rust has flexibility, we can pass the reference of the variable instead of the ownership itself.

In the above example, borrow_string simply receives a reference to a variable and can use it to do its thing, while the original variable can still be used for further processing. Is there anything fishy? The ownership of msg didn’t get transferred to println!, right? Well, technically, println! isn’t a function—it’s a macro. This macro converts the code into std::io::_print(format_args!("Hello {}\n", name));. The important part is that the macro doesn’t evaluate immediately; it passes &name (a reference) instead, meaning ownership isn’t transferred.
Next, you can also pass a mutable reference, which means the function can modify the value at the source.

It will print hello world.
Things to keep in mind while using mutable reference.
We cannot have other references at the same time


We can’t use the original variable while it’s mutably borrowed

We can’t create two mutable references to the same thing
Note: In situations where it makes sense to return a reference, we can do so—but not without specifying the lifetime. Below are examples of correct and incorrect usage:


And finally we can have generics in functions. In the below example we can pass types like String, int32 etc.

In summary, Rust’s ownership and borrowing system provides strong memory safety while offering flexibility. Whether you’re transferring ownership, borrowing immutably, or mutably, these concepts help prevent data races and ensure safe memory access.
Mutable references come with restrictions, like no other references or usage of the original variable while borrowed. Lifetimes ensure safe reference returns, and generics allow flexible, reusable functions.
While it may seem complex at first, Rust’s system ultimately helps you write fast, safe code with minimal runtime overhead.


