Below is a sample code that tries to return reference to Id in function get_names. How to fix when .internal is a None variant.
struct Id{
id:u32
}
struct Ids<'a>{
internal: Option<&'a[Id]>
}
// Return references to Ids if internal is Some variant.
// How to solve if internal is None variant.
// Don't use clone/copy for Id struct and get_names should return references of Ids.
fn get_names<'a>(ids: &'a Ids)->Vec<(&'a Id, String)>{
match ids.internal{
Some(x)=> x.into_iter().map(|x| (x, x.id.to_string())).collect(),
None=>todo!()
// Some nested call generates Id and should return their references.
// Expecting something like below. 0..10 is just an example whose lifetime
// is until end of get_names.
// None=>(0..10).map(|x| (&Id{id:x}, x.to_string())).collect()
}
}
pub fn main(){
let internal_ids = [Id{id:1}];
let ids = Ids{internal: Some(&internal_ids)};
_=get_names(&ids);
}
Is below code the best way to handle if internal is a None variant:
struct Id{
id:u32
}
struct Ids<'a>{
internal: Option<&'a[Id]>
}
enum IdType<'a>{
Value(Id),
Ref(&'a Id)
}
// Is this the bestway to wrap Id in an enum that either gives
// reference or direct value ?
fn get_names<'a>(ids: &'a Ids)->Vec<(IdType<'a>, String)>{
match ids.internal{
Some(x)=> x.into_iter().map(|x| (IdType::Ref(x), x.id.to_string())).collect(),
None=>(0..10).map(|x| (IdType::Value(Id{id:x}), x.to_string())).collect()
}
}
pub fn main(){
let internal_ids = [Id{id:1}];
let ids = Ids{internal: Some(&internal_ids)};
_=get_names(&ids);
}
Since Id is small data, the best thing to do is usually to return Ids by value always, instead of by reference. Adding #[derive(Copy)] to struct Id will make this easier to work with.
If you want to store data by reference in a struct, the correct types for that are Box, Rc or Arc.
When you use &, it forbids storing this data in the struct. Instead of a regular struct, you're getting a temporary view into data stored somewhere else, typically in a local variable in an outer function. This is a special case, which is very restrictive, to the point it's likely to make your whole program unusable and impossible to compile. In 99% of cases it's a design error to put temporary references in structs. Especially if you're just learning Rust, just don't put references in structs.
Anything allocated deep in the stack cannot be a reference to a parent stack frame. Your IdType is a suitable solution because it makes referencing optional, like Cow.
u32 doesn't allocate on the heap and is smaller than a reference.[1]
You're probably attempting premature optimization. Moreover, you're doing so while simultaneously ignoring other aspects of your code, like the creation of a Vec full of eagerly created Strings. Probably you should just not sweat it for now. Eventually you'll learn patterns that do avoid some unnecessary allocations.
You are using into_iter(), although you don't own the vector. Just use iter(), as into_iter() consumes the vector.
Next, you have a Rc<Vector<...>> and you want an Vec<.., Rc<Id>>, so you try to put the Id into an Rc, which fails, because you don't own the Id - that's why it doesn't work. To have an own Id, you can make Id Copy and it creates an owned copy automatically (which you don't want as far as I understand), you can make Id Clone and clone it manually (which you also don't want to do) or you can make a new Id completely manually as in
But what you might want to do, doesn't make sense, because you still would have the Ids in the vector in struct Idsand have them reference counted in the returned vector. That kind of Schrödinger-state is not allowed. Either something is reference counted or not.
All in all - the thing looks confusing. is the Vec<Id> in internal: Option<Rc<Vec<Id>>> really shared or did you put it into an Rc only because someone said Rc would solve the problem?
#[derive(Clone, Copy)]
struct Id {
id: u32,
}
struct Ids {
ids: Option<Vec<Id>>,
}
impl Ids {
fn get_names(&self) -> Vec<(Id, String)> {
let generated: Vec<Id>;
let ids = match self.ids.as_ref() {
Some(ids) => ids,
None => {
generated = (0..10).map(|n| Id { id: n }).collect();
&generated
}
};
ids.iter()
.copied()
.map(|id| (id, id.id.to_string()))
.collect()
}
}
You might want to save the generated IDs in the Ids struct, but you will need a &mut Ids or interior mutability. (The current implementation computes the IDs every call of get_names and never changes the Option to Some). You don’t need Rc or shared ownership.