[rust-dev] Safely writing iterators over idiomatic C structures

2013-12-06 Thread Edward Z . Yang
Imagine that we have a structure of the form:

typedef struct {
int payload1;
foo *link;
int payload2;
} foo;

This structure is characterized by two things:

1) It is a singly linked list, and thus has a simple ownership
structure which can be captured by Rust's owned pointers

2) The payload of this struct is interleaved with links, in
order to save space and an extra indirection.  The layout may
be fixed, by virtue of being exposed by a C library.

The question now is: can we write an Iteratormut foo for
the corresponding Rust structure foo, without using any unsafe code?

There is a fundamental problem with this structure: iterator
invalidation.  If we are able to issue a mut foo reference, then the
link field could get mutated.  Rust's borrow checker would reject this,
since the only possible internal state for the iterator (a mutable
reference to the next element) aliases with the mutable reference
returned by next().  I am not sure how to solve this without changing
the layout of the struct; perhaps there might be a way if one could
selectively turn off the mutability of some fields.

Suppose we are willing to change the struct, as per the extra::dlist
implementation, we still fall short of a safe implementation: the
internal state of the iterator utilizes a raw pointer (head), which
provides a function resolve() which simply mints a mutable reference to
the element in question. It seems to be using Rawlink to hide the fact
that it has its fingers on a mutable borrowed reference to the list.  It
recovers some safety by maintaining a mutable reference to the whole
list in the iterator structure as well, but it would be better if no
unsafe code was necessary at all, and I certainly don't feel qualified
to reason about the correctness of this code. (Though, I understand and
appreciate the fact that the back pointers have to be handled unsafely.)

So, is it possible? I just want (provably) safe, mutable iteration over
singly-linked lists...

Edward
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Safely writing iterators over idiomatic C structures

2013-12-06 Thread Huon Wilson

On 06/12/13 19:11, Edward Z. Yang wrote:

Imagine that we have a structure of the form:

 typedef struct {
 int payload1;
 foo *link;
 int payload2;
 } foo;

This structure is characterized by two things:

 1) It is a singly linked list, and thus has a simple ownership
 structure which can be captured by Rust's owned pointers

 2) The payload of this struct is interleaved with links, in
 order to save space and an extra indirection.  The layout may
 be fixed, by virtue of being exposed by a C library.

The question now is: can we write an Iteratormut foo for
the corresponding Rust structure foo, without using any unsafe code?

There is a fundamental problem with this structure: iterator
invalidation.  If we are able to issue a mut foo reference, then the
link field could get mutated.  Rust's borrow checker would reject this,
since the only possible internal state for the iterator (a mutable
reference to the next element) aliases with the mutable reference
returned by next().  I am not sure how to solve this without changing
the layout of the struct; perhaps there might be a way if one could
selectively turn off the mutability of some fields.

Suppose we are willing to change the struct, as per the extra::dlist
implementation, we still fall short of a safe implementation: the
internal state of the iterator utilizes a raw pointer (head), which
provides a function resolve() which simply mints a mutable reference to
the element in question. It seems to be using Rawlink to hide the fact
that it has its fingers on a mutable borrowed reference to the list.  It
recovers some safety by maintaining a mutable reference to the whole
list in the iterator structure as well, but it would be better if no
unsafe code was necessary at all, and I certainly don't feel qualified
to reason about the correctness of this code. (Though, I understand and
appreciate the fact that the back pointers have to be handled unsafely.)

So, is it possible? I just want (provably) safe, mutable iteration over
singly-linked lists...

Edward
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


One could have an Iterator(mut int, mut int), where the references 
point to just the fields. Off the top of my head:


struct List {
payload1: int,
next: Option~Foo,
payload2: f64
}

struct ListMutIterator'a {
elem: Option'a mut List
}

impl'a Iterator('a mut int, 'a mut f64) for ListMutIterator'a {
fn next(mut self) - Option('a mut int, 'a mut f64) {
 let elem = std::util::replace(self, None); // I think this 
might be necessary to get around the borrow checker.


 let (ret, next) = match elem {
 Some(List { payload1: ref mut payload1, next: ref mut 
next, payload2: ref mut payload2 }) = {

  (Some((payload1, payload2)), next.as_mut_ref())
 }
 None = (None, None)
 };
 *self = next;
 ret
}
}

(The List pattern match will look nicer if 
https://github.com/mozilla/rust/issues/6137 gets solved; `List { ref mut 
payload1, ref mut next, ref mut payload2 }`.)


I imagine this will end up being very ugly if there are more than just 2 
fields before and after, although one could easily replace the tuple 
with a struct `ListMutRef'a { payload1: 'a mut int, payload2: 'a mut 
f64 }`.


Huon
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Safely writing iterators over idiomatic C structures

2013-12-06 Thread Bill Myers
Maybe the language should be changed to allow Iterator to be changed to have a 
signature like this:
pub trait IteratorA {
    fn next'a('a mut self) - Option'a A;
}

Then you could return the mut by reborrowing and would be able to advance the 
iterator without issue afterwards. 
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Safely writing iterators over idiomatic C structures

2013-12-06 Thread Edward Z . Yang
OK, with a little bit of tweaking I have come up with a version
that seems to work:

struct List {
payload1: int,
next: Option~List,
payload2: f64
}

struct ListMutIterator'a {
elem: Option'a mut List
}

impl'a Iterator('a mut int, 'a mut f64) for ListMutIterator'a {
fn next(mut self) - Option('a mut int, 'a mut f64) {
let elem = std::util::replace(self, ListMutIterator{elem:None}); // 
I think this might be necessary to get around the borrow checker.

let (ret, next) = match elem {
ListMutIterator{elem: Some(List { payload1: ref mut payload1, 
next: ref mut next, payload2: ref mut payload2 })} = {
 (Some((payload1, payload2)), match *next {
None = None,
 Some(ref mut p) = Some(mut **p)
 })
}
ListMutIterator{elem: None} = (None, None)
};
*self = ListMutIterator{elem:next};
ret
}
}

Perhaps dlist should be updated to use this implementation strategy?

Edward
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Safely writing iterators over idiomatic C structures

2013-12-06 Thread Edward Z . Yang
Excerpts from Huon Wilson's message of 2013-12-06 00:26:36 -0800:
 One could have an Iterator(mut int, mut int), where the references 
 point to just the fields. Off the top of my head:

Sure. (This is not so good when there are a lot of fields.)

  impl'a Iterator('a mut int, 'a mut f64) for ListMutIterator'a {
  fn next(mut self) - Option('a mut int, 'a mut f64) {
   let elem = std::util::replace(self, None); // I think this 
 might be necessary to get around the borrow checker.
 
   let (ret, next) = match elem {
   Some(List { payload1: ref mut payload1, next: ref mut 
 next, payload2: ref mut payload2 }) = {
(Some((payload1, payload2)), next.as_mut_ref())

I fixed some of the egregious type-checking errors, but this one has me
stuck.  There is no as_mut_ref() in the version of Rust I'm running, and
the plausible replacement mut_ref() doesn't do what I want:

Huon.rs|24 col 37 error| mismatched types: expected 
`std::option::Optionmut List` but found `std::option::Optionmut ~List` 
(expected struct List but found ~-ptr)

(Because, of course, the thing inside the option is a ~List, not a
List).

Cheers,
Edward
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev