I'm in the process of learning Rust, and there's some functionality around ownership and references that consistently stumps me:
struct ThingOne {
value: u32
}
// Assume I can't change this signature to simply own thing_ref. Maybe
// it's from third-party code.
struct ThingTwo<'a> {
thing_ref: &'a ThingOne
}
fn test<'a>() -> ThingTwo<'a> {
let thing1 = ThingOne { value: 1 };
ThingTwo { thing_ref: &thing1 }
}
Obviously, this can't work -- thing1 is stack allocated, the reference can't safely escape the function. However, this pattern seems like a common one, and I can't find a good workaround. I could change the return type to a tuple (ThingOne, ThingTwo) so that the caller has moved ownership of thing1, but then I'd still run into issues trying to create that reference to thing1 when initializing the ThingTwo result. I could use an Rc<ThingOne> but that feels like a very heavy handed solution.
Is there an idiomatic way to handle this kind of code? Or is there some other approach that's better than the way that test() function is structured?
Related
I know this is a Rust newbie problem, but I actually can't wrap my head around it. I need to pass around a PhysicalDevice from the Vulkano library. The problem is, PhysicalDevice holds a reference:
pub struct PhysicalDevice<'a> {
instance: &'a Arc<Instance>,
device: usize,
}
So Rust won't compile my sacred code:
struct InstanceInfo<'a> {
instance: Arc<Instance>,
physical: PhysicalDevice<'a>,
// ...
}
// ...
fn instantiate() -> InstanceInfo<'static> {
// ...
let instance = Instance::new(None, &required_extensions, None).unwrap();
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
// ...
InstanceInfo { // cannot return value referencing local blah
instance,
physical,
// ...
}
}
How can I work around this?
So the reason for the error message is that instance is a local variable in your instantiate function. Because you aren't moving its ownership to the return value, it will be dropped at the end of the function.
But then if it gets dropped, any reference to it would be invalid. That's why Rust doesn't let you return something holding a reference to the local variable.
First, your struct is redundant, because PhysicalDevice already holds a reference for the instance. Even without the local variable problem, I think you'd run into an ownership problem.
Second, let's say you rewrite and get rid of your InstanceInfo struct and instead you want to just return a PhysicalDevice<'static>. Well if that's what you promise to the compiler, then you have to make sure that the instance you create will live for as long as the program lives.
You can do that either by having instance be a static variable of your module, or by creating it at the very beginning of the program and then simply pass a reference ot it around.
For example
fn main() {
let instance = Instance::new(None, blablabla).unwrap();
let physical = instantiate(&instance);
}
fn instantiate<'a>(instance: &'a Arc<Instance>) -> PhysicalDevice<'a> {
PhysicalDevice::enumerate(instance).next().unwrap();
}
Might have gotten a & wrong here or there, but this is the gist of it.
If you have to put everything into an extra struct, then I think what you want to do is to clone the Arc instead of just moving it.
TLDR: I want to replace a T behind &mut T with a new T that I construct from the old T
Note: please forgive me if the solution to this problem is easy to find. I did a lot of googling, but I am not sure how to word the problem correctly.
Sample code (playground):
struct T { s: String }
fn main() {
let ref mut t = T { s: "hello".to_string() };
*t = T {
s: t.s + " world"
}
}
This obviously fails because the add impl on String takes self by value, and therefore would require being able to move out of T, which is however not possible, since T is behind a reference.
From what I was able to find, the usual way to achieve this is to do something like
let old_t = std::mem::replace(t, T { s: Default::default() });
t.s = old_t + " world";
but this requires that it's possible and feasible to create some placeholder T until we can fill it with real data.
Fortunately, in my use-case I can create a placeholder T, but it's still not clear to me why is an api similar to this not possible:
map_in_place(t, |old_t: T| T { s: old_t.s + " world" });
Is there a reason that is not possible or commonly done?
Is there a reason [map_in_place] is not possible or commonly done?
A map_in_place is indeed possible:
// XXX unsound, don't use
pub fn map_in_place<T>(place: &mut T, f: impl FnOnce(T) -> T) {
let place = place as *mut T;
unsafe {
let val = std::ptr::read(place);
let new_val = f(val);
std::ptr::write(place, new_val);
}
}
But unfortunately it's not sound. If f() panics, *place will be dropped twice. First it will be dropped while unwinding the scope of f(), which thinks it owns the value it received. Then it will be dropped a second time by the owner of the value place is borrowed from, which never got the memo that the value it thinks it owns is actually garbage because it was already dropped. This can even be reproduced in the playground where a simple panic!() in the closure results in a double free.
For this reason an implementation of map_in_place would itself have to be marked unsafe, with a safety contract that f() not panic. But since pretty much anything in Rust can panic (e.g. any slice access), it would be hard to ensure that safety contract and the function would be somewhat of a footgun.
The replace_with crate does offer such functionality, with several recovery options in case of panic. Judging by the documentation, the authors are keenly aware of the panic issue, so if you really need that functionality, that might be a good place to get it from.
In my short Rust experience I ran into this pattern several times, and I'm not sure if the way I solve it is actually adequate...
Let's assume I have some trait that looks like this:
trait Container {
type Item;
fn describe_container() -> String;
}
And some struct that implements this trait:
struct ImAContainerType;
struct ImAnItemType;
impl Container for ImAContainerType {
type Item = ImAnItemType;
fn describe_container() -> String { "some container that contains items".to_string() }
}
This may be a container that has a knowledge about type of items it contains, like in this example, or, as another example, request which knows what type of response should be returned, etc.
And now I find myself in a situation, when I need to implement a function that takes an item (associated type) and invokes a static function of the container (parent trait). This is the first naive attempt:
fn describe_item_container<C: Container>(item: C::Item) -> String {
C::describe_container()
}
This does not compile, because associated types are not injective, and Item can have several possible Containers, so this whole situation is ambiguous. I need to somehow provide the actual Container type, but without providing any container data. I may not have the container data itself at all when I invoke this function!
In search for a solution, I find the documentation for std::marker::PhantomData. It says:
PhantomData allows you to describe that a type acts as if it stores a value of type T, even though it does not.
This has to be the Rust's replacement for Haskell's Proxy type, right? Let's try to use it:
fn describe_item_container<C: Container>(container: PhantomData<C>, item: C::Item) -> String {
C::describe_container()
}
let s = describe_item_container(PhantomData::<PhantomData<ImAContainerType>>, ImAnItemType);
println!("{}", s);
Compiling... Error:
error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied
I ask #rust-beginners and get a response: PhantomData is not meant to be used that way at all! Also I got an advice to simply make a backward associated type link from Item to Container. Something like this:
trait Item {
type C: Container;
}
fn describe_item_container<I: Item>(item: I) -> String {
I::C::describe_container()
}
It should work, but makes things much more complicated (especially for cases when item can be placed in different container kinds)...
After a lot more experimentation, I do the following change and everything compiles and works correctly:
let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType);
println!("{}", s);
The change is ::<PhantomData<ImAContainerType>> to ::<ImAContainerType>.
Playground example.
It works, but now I'm completely confused. Is this the correct way to use PhantomData? Why does it work at all? Is there some other, better way to provide type-only argument to a function in Rust?
EDIT: There is some oversimplification in my example, because in that particular case it would be easier to just invoke ImAContainerType::describe_container(). Here is some more complicated case, when the function actually does something with an Item, and still requires container type information.
If you want to pass a type argument to a function, you can just do it. You don't have to leave it out to be inferred.
This is how it looks for your second example (playground):
fn pack_item<C: Container>(item: C::Item) -> ItemPacket {
ItemPacket {
container_description: C::describe_container(),
_payload: item.get_payload(),
}
}
fn main() {
let s = pack_item::<ImAContainerType>(ImAnItemType);
println!("{}", s.container_description);
let s = pack_item::<ImAnotherContainerType>(ImAnItemType);
println!("{}", s.container_description);
}
I have some experience in C, but I'm new to Rust. What happens under the hood when I pass a struct into a function and I return a struct from a function? It seems it doesn't "copy" the struct, but if it isn't copied, where is the struct created? Is it in the stack of the outer function?
struct Point {
x: i32,
y: i32,
}
// I know it's better to pass in a reference here,
// but I just want to clarify the point.
fn copy_struct(p: Point) {
// Is this return value created in the outer stack
// so it won't be cleaned up while exiting this function?
Point {.. p}
}
fn test() {
let p1 = Point { x: 1, y: 2 };
// Will p1 be copied or does copy_struct
// just use a reference of the one created on the outer stack?
let p2 = copy_struct(p1);
}
As a long time C programmer also playing with Rust recently, I understand where you're coming from. For me the important thing to understand was that in Rust value vs reference are about ownership, and the compiler can adjust the calling conventions to optimize around move semantics.
So you can pass a value without it making a copy on the stack, but this moves the ownership to the called function. It's still in the calling functions stack frame, and from a C ABI perspective it's passing a pointer, but the compiler enforces that the value is never used again upon return.
There's also return value optimization, where the calling function allocates space and the pointer is passed to the caller which can fill out the return value there directly. This is the sort of thing a C programmer would be used to handling manually.
So the safety of the ownership rules and borrow checker, combined with the lack of a fixed guaranteed ABI/calling convention, allow the compiler to generate efficient call sites. And generally you worry more about ownership and lifetime, then needing to try and be clever about function call stack behavior.
I’m not sure what you are asking.
If your question is about what happens with the value you created from the point of view of you as a programmer, then the answer is it is moved (unless it implements Copy). You might want to go through some basic rust tutorials to grasp this concept.
If you are asking about what happens under the hood, then I’m afraid there might be no single answer. I believe, conceptually, the value is being copied using something like memcpy, but then the optimizer might kick in and eliminate this. I don’t think there is something like a specification regarding this, and it might be better to consider this an implementation detail.
I did some experiments and found that rust will do return value optimization whenever the size of a struct is larger than 16, even if the struct has Copy trait.
#[derive(Copy, Clone)]
struct NoRVO {
_a: [u8; 16],
}
#[derive(Copy, Clone)]
struct HasRVO {
_a: [u8; 17],
}
#[inline(never)]
fn new_no_rvo() -> NoRVO {
let no_rvo = NoRVO { _a: [0; 16] };
println!("callee no_rvo: {:p}", &no_rvo);
no_rvo
}
#[inline(never)]
fn new_has_rvo() -> HasRVO {
let has_rvo = HasRVO { _a: [0; 17] };
println!("callee has_rvo: {:p}", &has_rvo);
has_rvo
}
fn main() {
let has_rvo = new_has_rvo();
println!("caller has_rvo: {:p}", &has_rvo);
let no_rvo = new_no_rvo();
println!("caller no_rvo: {:p}", &no_rvo);
}
An example of the output:
callee has_rvo: 0x13791ff380
caller has_rvo: 0x13791ff380
callee no_rvo: 0x13791ff2b0
caller no_rvo: 0x13791ff3e8
Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.
I'm trying to write a container structure in Rust where its elements also store a reference to the containing container so that they can call methods on it. As far as I could figure out, I need to do this via Rc<RefCell<T>>. Is this correct?
So far, I have something like the following:
struct Container {
elems: ~[~Element]
}
impl Container {
pub fn poke(&mut self) {
println!("Got poked.");
}
}
struct Element {
datum: int,
container: Weak<RefCell<Container>>
}
impl Element {
pub fn poke_container(&mut self) {
let c1 = self.container.upgrade().unwrap(); // Option<Rc>
let mut c2 = c1.borrow().borrow_mut(); // &RefCell
c2.get().poke();
// self.container.upgrade().unwrap().borrow().borrow_mut().get().poke();
// -> Error: Borrowed value does not live long enough * 2
}
}
fn main() {
let container = Rc::new(RefCell::new(Container{ elems: ~[] }));
let mut elem1 = Element{ datum: 1, container: container.downgrade() };
let mut elem2 = Element{ datum: 2, container: container.downgrade() };
elem1.poke_container();
}
I feel like I am missing something here. Is accessing the contents of a Rc<RefCell<T>> really this difficult (in poke_container)? Or am I approaching the problem the wrong way?
Lastly, and assuming the approach is correct, how would I write an add method for Container so that it could fill in the container field in Element (assuming I changed the field to be of type Option<Rc<RefCell<T>>>? I can't create another Rc from &mut self as far as I know.
The long chain of method calls actually works for me on master without any changes, because the lifetime of "r-values" (e.g. the result of function calls) have changed so that the temporary return values last until the end of the statement, rather than the end of the next method call (which seemed to be how the old rule worked).
As Vladimir hints, overloadable dereference will likely reduce it to
self.container.upgrade().unwrap().borrow_mut().poke();
which is nicer.
In any case, "mutating" shared ownership is always going to be (slightly) harder to write in Rust that either single ownership code or immutable shared ownership code, because it's very easy for such code to be memory unsafe (and memory safety is the core goal of Rust).