1

I am caught between two different issues/bugs, and can't come up with a decent solution. Any help would be greatly appreciated

Context, FFI, and calling a lot of C functions, and wrapping C types in rust structs.

The first problem is ICE: this path should not cause illegal move.

This is forcing me to do all my struct-wrapping using & references as in:

pub struct CassResult<'a> {
    result:&'a cql_ffi::CassResult
}

Instead of the simpler, and preferable:

pub struct CassResult {
    result:cql_ffi::CassResult
}

Otherwise code like:

pub fn first_row(&self) -> Result<CassRow,CassError> {unsafe{
    Ok(CassRow{row:*cql_ffi::cass_result_first_row(self.result)})
}}

Will result in:

error: internal compiler error: this path should not cause illegal move
Ok(CassRow{row:*cql_ffi::cass_result_first_row(self.result)})
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So, I go ahead and wrap everything using lifetime managed references, and all is not-horrible until I try to implement an iterator. At which point I see no way around this problem.

method next has an incompatible type for trait: expected concrete lifetime, found bound lifetime parameter

So given those two conflicting issues, I am totally stuck and can't find any way to implement a proper rust iterator around a FFI iterator-like construct.

Edit: With Shep's suggestion, I get:

pub struct CassResult {
    pub result:cql_ffi::CassResult
}

and

pub fn get_result(&mut future:future) -> Option<CassResult> {unsafe{
    let result:&cql_ffi::CassResult = &*cql_ffi::cass_future_get_result(&mut future.future);
    Some(CassResult{result:*result})
}}

but then get:

error: cannot move out of borrowed content
Some(CassResult{result:*result}

Is there any way to make that pattern work? It's repeated all over this FFI wrapping code.

Tupshin Harper
  • 1,267
  • 9
  • 12
  • It’d be easier to make comment on this if you gave the full code which you’re wanting to have work. I suspect that Stack Overflow isn’t a good place for figuring this out, either; I recommend you try the Rust IRC channel. – Chris Morgan Jan 28 '15 at 03:32
  • As a workaround for your first bug, you may want to go through a reference first. Instead of `foo: T = unsafe { *ptr }`, try `foo: &mut T = unsafe { &mut *ptr }` (redundant type annotations added for clarity). – Shepmaster Jan 28 '15 at 03:36
  • I think I ran into something like this and in my particular case I was able to solve it (and it made sense to) by implementing `Copy` for the type, which I guess for you would be `CassResult`. – Jorge Israel Peña Jan 28 '15 at 03:56
  • Ding ding ding. Shep's workaround for this ICE is (I believe) the ideal answer, given my predicament. FWIW, the very work-in-progress code is https://github.com/tupshin/cql-ffi-safe – Tupshin Harper Jan 28 '15 at 04:51
  • or not quite. added an edit. – Tupshin Harper Jan 28 '15 at 05:14

2 Answers2

0

Only a partial answer: use the "streaming iterator" trait and macro.

I have had a similar problem making Rust bindings around the C mysql API. The result is code like this, instead of native for syntax:

let query = format!("SELECT id_y, value FROM table_x WHERE id = {}", id_x);
let res = try!(db::run_query(&query));
streaming_for!( row, res.into_iter(), {
    let id_y: usize = try!(row.convert::<usize>(0));
    let value: f64 = try!(row.convert::<f64>(1));
});

Here res holds the result and frees memory on drop. The lifetime of row is tied to res:

/// Res has an attached lifetime to guard an internal pointer.
struct Res<'a>{ p: *mut c_void }
/// Wrapper created by into_iter()
struct ResMoveIter<'a>{ res: Res<'a> }
impl<'a> /*StreamingIterator<'a, Row<'a>> for*/ ResMoveIter<'a>{
    /// Get the next row, or None if no more rows
    pub fn next(&'a mut self) -> Option<Row<'a>>{
        ...
    }
}
#[unsafe_destructor]
impl<'a> Drop for Res<'a>{
    fn drop(&mut self){
        ...
    }
}
dhardy
  • 11,175
  • 7
  • 38
  • 46
  • While I'd really like that ICE to be fixed, I do like the streaming iterator idea. I'll give that a try and accept this answer if it works. tyvm – Tupshin Harper Jan 28 '15 at 19:28
0

To answer my own question. The only decent answer was a way around the original ICE, but as thepowersgang comments, the correct way to do this now is to use :std::ptr::read, so using that approach, no ICE, and hopefully progress.

Tupshin Harper
  • 1,267
  • 9
  • 12