Lifetime of function reference in Rust - rust

So I'm trying to squeeze in callbacks to this enum variant (Visual) - which will be stored in a vector as seen within the struct EntityComponents:
enum Component {
Position([f64; 2]),
Visual(& Fn(Entity) -> ()),
}
struct EntityComponents {
components_of_entity: HashMap<TypeId, Vec<Component>>,
}
However, Rust requires me to provide explicit lifetime parameters here.
My idea is that I want the function reference to live at least as long as its argument (the Entity), but I have no idea how the syntax for that would look like? Is it even possible?
The idea is that as long as an Entity has a Visual component, we can use this callback to render it!

A few things:
You used &Fn() instead of fn(). The former is a trait object of the Fn trait, the latter is a good ol' function pointer. While the former is more general (in particular, it supports closures), it's rather unusual to store a reference to a closure that lives somewhere else.
Therefore, if you want to store closures as well, you'd probably use a Box<Fn()> which solves your lifetime problems.
If you don't care about closures, but just want to save function pointer, you can simply write Visual(fn(Entity)),. Function pointer always have a static lifetime. So this solves your lifetime problems as well.
But what if you really want to store a reference to an Fn trait object, which lives somewhere else? The solution depends on where it lives:
In Entity: see here
somehere else: use a lifetime <'a>
You wrote Fn(Entity) -> (). The -> () is always useless, you can omit it.

Related

Why does the Rust standard library implement traits for both Thing and &Thing?

I was reading the question The trait `std::fmt::Write` is not implemented for `Stdout` when it should be where the asker noted that the rust documentation shows that the std::io::Write trait is implemented for both &Stdout and Stdout.
I don't understand why this is necessary or how you would use it. Isn't everything you define for Thing always implemented for &Thing? Why would you implement something for &Thing without implementing it for it's definition?
Isn't everything you define for Thing always implemented for &Thing?
No, an implementation for a type T will not automatically implement anything for &T. Now, sometimes blanket implementations can kick in, and if you pass a &&T to a function expecting a &T, then Rust will insert dereferences for you, but that does not mean the trait was implemented for &T, just that Rust helped you out a bit.
Why would you implement something for &Thing without implementing it for it's definition?
There's a very good example of that which we use all the time: String::from.
impl From<&str> for String {
fn from(value: &str) -> String {
...
}
}
From::<T>::from takes an argument, by value. No references or anything, just straight-up a value of type T. So we can never write a From<str> implementation for anything, since str is unsized and hence cannot be a function argument on its own. But it makes perfect sense to convert a &str to a String: that's just making an owned copy of the string.
Building on #Silvio-Mayolo's answer: in this particular case, there was originally just an implementation for Stdout; and the more-flexible implementation for &Stdout was added later, which provides a strict superset of functionality, notably allowing you to share the reference between threads. The original implementation can't be removed without breaking backwards compatibility with existing code using the by-value implementation (auto-deref isn't perfect, you would still need to go back and add an & in some situations).

Tell the Rust compiler that the lifetime of a parameter is always identical to a struct's lifetime

Sorry if this is obvious, I'm starting out with Rust.
I'm trying to implement a simple Composition relationship (one object is the single owner of another one, and the inner object is destroyed automatically when the outer object dies).
I originally thought it would be as simple as declaring a struct like this:
struct Outer {
inner: Inner
}
To my knowledge, that does exactly what I want: the inner attribute is owned by the outer struct, and will be destroyed whenever the outer object disappears.
However, in my case, the Inner type is from a library, and has a lifetime parameter.
// This is illegal
struct Outer {
inner: Inner<'a>
}
// I understand why, I'm using an undeclared lifetime parameter
I have read a bit on lifetime parameters (but I'm not yet completely used to them), and I'm unsure whether there is some syntax to tell the Rust compiler that “the lifetime this field expects is its owner's“, or whether it's just not possible (in which case, what would be the proper way to architecture this code?).
Edit: more detailed situation
I'm writing a project with Vulkano. I want to bundle multiple structures into a single structure so I can pass all at once to functions throughout the project.
Here, I have:
The Engine struct, which should hold everything
The Instance struct, which represents the Vulkan API
The PhysicalDevice struct, which represents a specific GPU, and can only be used as long as its matching Instance exists
The struct I'm struggling with is PhysicalDevice:
// https://github.com/vulkano-rs/vulkano/blob/c6959aa961c9c4bac59f53c99e73620b458d8d82/vulkano/src/device/physical.rs#L297
pub struct PhysicalDevice<'a> {
instance: &'a Arc<Instance>,
device: usize,
}
I want to create a struct that looks like:
pub struct Engine {
instance: Arc<Instance>,
device: PhysicalDevice,
}
Because the Engine struct owns PhysicalDevice as well as Arc<Instance>, and the instance referenced by PhysicalDevice is the same as the one referenced by the Engine, the PhysicalDevice's lifetime requirement should always be valid (since the contained instance cannot be freed before the Engine is freed).
I don't have very good reason of using this architecture, apart from the fact that this is the standard way of bundling related data in other languages. If this not proper "good practices" in Rust, I'm curious as to what the recommended approach would be.
“the lifetime this field expects is its owner's“,
This is impossible.
Generally, you should understand any type with a lifetime parameter (whether it is a reference &'a T or a struct Foo<'a> or anything else) as pointing to something elsewhere and which lives independently.
In your case, whatever Inner borrows (whatever its lifetime is about) cannot be a part of Outer. This is because if it was, Outer would be borrowing parts of itself, which makes Outer impossible to use normally. (Doing so requires pinning, preventing the struct from moving and thereby invalidating the references, which is not currently possible to set up without resorting to unsafe code.)
So, there are three cases you might have:
Your full situation is
struct Outer {
some_data: SomeOtherType,
inner: Inner<'I_want_to_borrow_the_some_data_field>,
}
This is not possible in basic Rust; you need to either
put some_data somewhere other than Outer,
use the ouroboros library which provides mechanisms to build sound self-referential structs (at the price of heap allocations and a complex interface),
or design your data structures differently in some other way.
The data Inner borrows is already independent. In that case, the correct solution is to propagate the lifetime parameter.
struct Outer<'a> {
inner: Inner<'a>,
}
There is not actually any borrowed data; Inner provides for the possibility but isn't actually using it in this case (or the borrowed data is a compile-time constant). In this case, you can use the 'static lifetime.
struct Outer {
inner: Inner<'static>,
}
You can use a bound on Self:
struct Outer<'a> where Self: 'a {
inner: Inner<'a>
}

Can you specify return type mutability in Rust?

Is it possible to specify the mutability of the assigned variable in rust? Something like
fn new(len: usize) -> Thing { ... }
fn new_mut(len: usize) -> mut Thing { ... }
I have a specific case where knowing the mutability of a type can be used to make several optimizations under the hood for my data structure.
Trying to enforce mutability manually is possible, but seems quite inelegant, especially when the concept of mutability is an intrinsic part of the rust language already. And you end up in weird situations like this:
// Thing::new() returns a data structure with an immutable backing type,
// but the code below looks like it should be mutable.
let mut foo = Thing::new(5);
In this case, I either have to choice of trying to figure out if someone tried to make a mutable reference to my immutable Thing manually (and panicking I suppose), or by making new return a wrapper over Thing that hides all mutable functions (which means the mut keyword is rendered pointless and misleading).
I think you have some misconception: mutability of a return type is not and should NOT be part of the function signature, mutability is always decided on the caller side.
The return type is a description of the memory slot returned to the caller when the call stack returns after executing the function. Ownership of the returned type is fully transferred, e.g. a Thing is fully moved to the caller. How the caller handles the returned memory cell is not the concern of the called function because it's already finished and returned. There's no such thing as mutable or immutable return type, mutability is always related to memory slots. In your example this is only decided at declaring variable foo that defines the memory slot of the result type on the caller side. As long as you have full ownership of a data structure, you're free to decide or even change mutability of the data.
What you're looking for is maybe a separate type specialized for optimization.

Opposite of Borrow trait for Copy types?

I've seen the Borrow trait used to define functions that accept both an owned type or a reference, e.g. T or &T. The borrow() method is then called in the function to obtain &T.
Is there some trait that allows the opposite (i.e. a function that accepts T or &T and obtains T) for Copy types?
E.g. for this example:
use std::borrow::Borrow;
fn foo<T: Borrow<u32>>(value: T) -> u32 {
*value.borrow()
}
fn main() {
println!("{}", foo(&5));
println!("{}", foo(5));
}
This calls borrow() to obtain a reference, which is then immediately dereferenced.
Is there another implementation that just copies the value if T was passed in, and dereferences if &T was given? Or is the above the idiomatic way of writing this sort of thing?
There is not really an inverse trait for Borrow, because it's not really useful as a bound on functions the same way Borrow is. The reason has to do with ownership.
Why is "inverse Borrow" less useful than Borrow?
Functions that need references
Consider a function that only needs to reference its argument:
fn puts(arg: &str) {
println!("{}", arg);
}
Accepting String would be silly here, because puts doesn't need to take ownership of the data, but accepting &str means we might sometimes force the caller to keep the data around longer than necessary:
{
let output = create_some_string();
output.push_str(some_other_string);
puts(&output);
// do some other stuff but never use `output` again
} // `output` isn't dropped until here
The problem being that output isn't needed after it's passed to puts, and the caller knows this, but puts requires a reference, so output has to stay alive until the end of the block. Obviously you can always fix this in the caller by adding more blocks and sometimes a let, but puts can also be made generic to let the caller delegate the responsibility of cleaning up output:
fn puts<T: Borrow<str>>(arg: T) {
println!("{}", arg.borrow());
}
Accepting T: Borrow for puts gives the caller the flexibility to decide whether to keep the argument around or to move it into the function.¹
Functions that need owned values
Now consider the case of a function that actually needs to take ownership:
struct Wrapper(String);
fn wrap(arg: String) -> Wrapper {
Wrapper(arg)
}
In this case accepting &str would be silly, because wrap would have to call to_owned() on it. If the caller has a String that it's no longer using, that would needlessly copy the data that could have just been moved into the function. In this case, accepting String is the more flexible option, because it allows the caller to decide whether to make a clone or pass an existing String. Having an "inverse Borrow" trait would not add any flexibility that arg: String does not already provide.
But String isn't always the most ergonomic argument, because there are several different kinds of string: &str, Cow<str>, Box<str>... We can make wrap a little more ergonomic by saying it accepts anything that can be converted into a String.
fn wrap<T: Into<String>>(arg: T) -> Wrapper {
Wrapper(arg.into())
}
This means you can call it like wrap("hello, world") without having to call .to_owned() on the literal. Which is not really a flexibility win -- the caller can always call .into() instead without loss of generality -- but it is an ergonomic win.
What about Copy types?
Now, you asked about Copy types. For the most part the arguments above still apply. If you're writing a function that, like puts, only needs a &A, using T: Borrow<A> might be more flexible for the caller; for a function like wrap that needs the whole A, it's more flexible to just accept A. But for Copy types the ergonomic advantage of accepting T: Into<A> is much less clear-cut.
For integer types, because generics mess with type inference, using them usually makes it less ergonomic to use literals; you may end up having to explicitly annotate the types.
Since &u32 doesn't implement Into<u32>, that particular trick wouldn't work here anyway.
Since Copy types are readily available as owned values, it's less common to use them by reference in the first place.
Finally, turning a &A into an A when A: Copy is as simple as just adding *; being able to skip that step is probably not a compelling enough win to counterbalance the added complexity of using generics in most cases.
In conclusion, foo should almost certainly just accept value: u32 and let the caller decide how to get that value.
See also
Is it more conventional to pass-by-value or pass-by-reference when the method needs ownership of the value?
¹ For this particular function you'd probably want AsRef<str>, because you're not relying on the extra guarantees of Borrow, and the fact that all T implements Borrow<T> isn't usually relevant for unsized types such as str. But that is beside the point.
With the function you have you can only use a u32 or a type that can be borrowed as u32.
You can make your function more generic by using a second template argument.
fn foo<T: Copy, N: Borrow<T>>(value: N) -> T {
*value.borrow()
}
This is however only a partial solution as it will require type annotations in some cases to work correctly.
For example, it works out of the box with usize:
let v = 0usize;
println!("{}", foo(v));
There is no problem here for the compiler to guess that foo(v) is a usize.
However, if you try foo(&v), the compiler will complain that it cannot find the right output type T because &T could implement several Borrow traits for different types. You need to explicitly specify which one you want to use as output.
let output: usize = foo(&v);

Why calling iter on array of floats gives "temporary value is freed" when using f64::from_bits?

I encountered a lifetime error which I am not able to explain why it is emitted by the compiler. I need this (which works fine):
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[3.14].iter()
}
However, when I try to use a float value which is transmuted from a specific byte representation using from_bits, like this:
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[f64::from_bits(0x7fffffffffffffff)].iter()
}
it gives me "creates a temporary which is freed while still in use". Playground here (stable 1.45.2).
My reasoning was that since f64 is Copy type (and it indeed works as expected if I use a constant value), this should work because no freeing should be performed on this value.
So the question is why the compiler emits the error in the second case?
Thanks for any pointers and explanations!
P.S. I need the iterator over references because it fits nicely with my other API.
Your code has two related problems:
You return a reference to a temporary object that go out of scope at the end of the function body.
Your return type contains a lifetime parameter that isn't bound to any function input.
References can only live as long as the data they point to. The result of the method call f64::from_bits(0x7fffffffffffffff) is a temporary object which goes out of scope at the end of the expression. Returning a reference to a temporary value or a local variable from a function is not possible, since the value referred to won't be alive anymore once the function returns.
The Copy trait, or whether the object is stored on the heap, is completely unrelated to the fact that values that go out of scope can no longer be referred to. Any value created inside a function will go out of scope at the end of the function body, unless you move it out of the function via the return value. However, you need to move ownership of the value for this to work – you can't simply return a reference.
Since you can't return a reference to any value created inside your function, any reference in the return value necessarily needs to refer to something that was passed in via the function parameters. This implies that the lifetime of any reference in the return value needs to match the lifetime of some reference that was passed to the function. Which gets us to the second point – a lifetime parameter that only occurs in the return type is always an error. The lifetime parameter is chosen by the calling code, so you are essentially saying that your function returns a reference that lives for an arbitrary time chosen by the caller, which is only possible if the reference refers to static data that lives as long as the program.
This also gives an explanation why your first example works. The literal [3.14] defines a constant static array. This array will live as long as the program, so you can return references with arbitrary lifetime to it. However, you'd usually express this by explicitly specifying the static lifetime to make clear what is happening:
fn iter() -> impl Iterator<Item = &'static f64> {
[3.14].iter()
}
A lifetime parameter that only occurs in the return value isn't ever useful.
So how do you fix your problem? You probably need to return an iterator over an owned type, since your iter() function doesn't accept any arguments.
It is Copy, it is semantically copied, and that is indeed the problem, as it semantically only exists on the stack until function returns, reference is now, semantically, pointing to memory that is outside the stack and is very likely to be overwritten soon, and if Rust allowed it, that would result in undefined behaviour.
On top of that, from_bits isn't const, and that means that you cannot convert static values at compile time, it is a runtime operation. Why do you want to convert each time when you already know the value?
Why is this the case anyway?
from_bits:
This is currently identical to transmute::<u64, f64>(v) on all
platforms.
If you take a look at transmute, you will find:
transmute is semantically equivalent to a bitwise move of one type
into another. It copies the bits from the source value into the
destination value, then forgets the original. It's equivalent to C's
memcpy under the hood, just like transmute_copy.
While generated code may indeed just be a simple reinterpretation of static value, Rust cannot semantically allow this value to be moved onto stack, then dropped, while a reference is still pointing to it.
Solution.
Since you want to return aNaN, you should just do the same thing you did in the first example:
fn iter<'a>() -> impl Iterator<Item = &'a f64> {
[f64::NAN].iter()
}
This will iterate over a static slice directly, and there will be no issues.

Resources