سؤال

Edit: This question was too complicated obviously. Here's a much simpler example that explains what I'm trying to ask:

#[feature(macro_rules)];    
macro_rules! trace(
  ($($arg:tt)*) => (
    { let x = ::std::io::stdout().write_line(format_args!(::std::fmt::format, $($arg)*)); println!("{}", x); }
  );
)

#[deriving(Show)]
struct Foo<T> {
  data: ~T
}

impl<T> Foo<T> {

  // Notice how the 'pontless_marker' variable is passed into this function for
  // no reason other than that it allows us to copy the lifetime scope of the
  // 'marker' variable in the test below, so the lifetime of the returned pointer
  // is valid for that block.
  fn returns_to_scope_with_marker<'a>(&'a self, pointless_marker:&'a int) -> &'a ~T {
    return &self.data;
  }

  // This doesn't work, but it should. You should be able to invoke this function
  // as something like: let marked = bar.returns_to_scope::<LIFETIME???>();
  /*
  fn returns_to_scope<'a>(& self) -> &'a ~T {
    return &self.data;
  }
  */
}

#[test]
fn test_lifetime_return_scope() {
  let bar = Foo { data: ~Foo { data: ~10 } };
  {
    let marker = 10;
    let marked = bar.returns_to_scope_with_marker(&marker);
    trace!("{:?}", marked);
  }
}

See the 'pointless_marker' in the example above.

This is what I want to avoid. If the function is also typed as blah<'a>, I feel like I should be able to explicitly invoke it using x.blah::<Something goes here>().

Is that just flat out impossible?

هل كانت مفيدة؟

المحلول

Ok here is the code that will hopefully solve your problems:

#[feature(macro_rules)];    
//Macro rules is unchanged

#[deriving(Show)]
struct Foo<'a, T> {
  data: &'a T
}

impl<'a, T> Foo<'a, T> {

  fn returns_to_scope(&'a self) -> &'a T {
    self.data
  }
}

fn main() {
  let bar = Foo { data: &Foo { data: &10 } };
  {
    let marked = bar.returns_to_scope();
    trace!("{:?}", marked);
  }
}

I'll go line by line (but not in order) and explain what was the problem and how to solve it.

First you said you wanted to avoid warnings and to avoid the pointless variable sent. I agree those parameters are unnecessary. To avoid adding variable to the function, and it's parameters a life time parameter will be added to the impl and the struct definitions, in following lines:

 struct Foo<'a, T> 
 ...
 impl<'a, T> Foo<'a, T> 
 // note: impl<'a,T> is mandatory, because we can say <'a, int> for example
 //       which is not what we want 

'a is a lifetime param it acts as another generic type (i.e. T).

*If we have 'a lifetime we kinda need to tie it to something... We'll return back to this part once fn returns_to_scope is fixed later on in the text, but it's obvious that data will get the lifetime since - it's the only value in the struct Foo and it is returned out of a function that requires something a lot like &'a T.

Now, we can look a bit better at the fn returns_to_scope function. If you look at your original function you almost had it right.

 fn returns_to_scope<'a>(& self) -> &'a ~T {
   return &self.data;
 }

First thing that strikes me the wrong way is that you have a &~ pointer (&'a ~T). Why? You do understand it's a stack pointer to a pointer that allocates on the heap? It's not really useful unless you are doing some pointer pointing acrobatics, but I assume it's a mistake. If it's not well, you can always make a ~T value and borrow it.

So you want your returns_to_scope to return a self.data field in some way, if you are returning a field that belongs to a struct Foo, I think it's more sensible to use same lifetime as the struct Foo, the field belongs to; So I changed the description to:

fn returns_to_scope(&self) -> &'a T

I omit the <'a> generic parameter because I don't need to generalize this function over 'a lifetime, 'a lifetime is part of the struct we get it for free by borrowing the struct. However I forgot to say which lifetime on &self so compiler will complain and I'll just change &self to be annotated with 'a life time.

fn returns_to_scope(&'a self) -> &'a T

For the finishing touch, since Rust allows to return the field just by omitting ; I can write:

fn returns_to_scope(&'a self) -> &'a T {
   self.data
}

Instead of return self.data;.

Now we see that our field in struct is a natural candidate for 'a life time so

data: &'a T

is added to the struct Foo and all pieces are now in place.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top