I'm attempting to add the kRing function (amongst others) to an existing Rust project that implements a C-binding of the Uber H3 library. Here's the original source: https://github.com/scottmmjackson/h3api
Here are my very newbish additions:
extern crate libc;
#[macro_use]
extern crate failure;
use std::ffi::CString;
use std::fmt;
use std::str;
use libc::{c_char, c_int, c_ulonglong, size_t};
#[link(name = "h3")]
extern "C" {
// Indexing.
fn geoToH3(g: *const GeoCoordInternal, res: c_int) -> c_ulonglong;
fn h3ToGeo(h3: c_ulonglong, g: *mut GeoCoordInternal);
fn h3ToGeoBoundary(h3: c_ulonglong, gp: *mut GeoBoundaryInternal);
// Inspection.
fn h3GetResolution(h: c_ulonglong) -> c_int;
fn h3GetBaseCell(h: c_ulonglong) -> c_int;
fn stringToH3(str: *const c_char) -> c_ulonglong;
fn h3ToString(h: c_ulonglong, str: *const c_char, sz: size_t);
fn h3IsValid(h: c_ulonglong) -> c_int;
fn h3IsResClassIII(h: c_ulonglong) -> c_int;
fn h3IsPentagon(h: c_ulonglong) -> c_int;
// Traversal.
fn h3Distance(origin: c_ulonglong, h3: c_ulonglong) -> c_int;
fn kRing(origin: c_ulonglong, k: c_int, h3: [ *mut c_ulonglong; 6 ]);
// Hierarchy.
fn h3ToParent(h: c_ulonglong, parentRes: c_int) -> c_ulonglong;
}
const DEG_TO_RAD: f64 = std::f64::consts::PI / 180.0;
const RAD_TO_DEG: f64 = 180.0 / std::f64::consts::PI;
// Maximum number of cell boundary vertices. The worst case is a pentagon: 5 original verts
// and 5 edge crossings.
const MAX_CELL_BNDRY_VERTS: usize = 10;
/// H3Index is a point in the H3 geospatial indexing system.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct H3Index(u64);
impl H3Index {
/// Creates a new `H3Index` from the given point. If the point is not a valid index in
/// H3 then `None` is returned.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// let h = H3Index::new(0x850dab63fffffff).unwrap();
/// ```
pub fn new(h: u64) -> Result<Self, Error> {
let valid;
unsafe {
valid = h3IsValid(h);
}
if valid == 0 {
return Err(Error::InvalidIndex { value: h });
}
Ok(Self(h))
}
/// Converts a string to an H3 index.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// assert_eq!(
/// H3Index::from_str("0x850dab63fffffff").unwrap(),
/// H3Index::new(0x850dab63fffffff).unwrap()
/// )
/// ```
pub fn from_str(s: &str) -> Result<Self, Error> {
let c_str = match CString::new(s) {
Ok(c_str) => c_str,
Err(_) => {
return Err(Error::InvalidString {
value: s.to_owned(),
})
}
};
let h;
unsafe {
h = stringToH3(c_str.as_ptr());
}
if h == 0 {
return Err(Error::InvalidString {
value: s.to_owned(),
});
}
return Ok(H3Index(h));
}
/// Finds the centroid of the index.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::{GeoCoord, H3Index};
///
/// let h = H3Index::new(0x850dab63fffffff).unwrap();
/// assert_eq!(h.to_geo(), GeoCoord::new(67.15092686397712, -168.39088858096966));
/// ```
pub fn to_geo(self) -> GeoCoord {
let mut geo = GeoCoordInternal::new(0.0, 0.0);
unsafe {
h3ToGeo(self.0, &mut geo);
}
geo.to_deg()
}
/// Finds the boundary of the index.
///
/// # Example
///
/// ```
/// // TODO
/// ```
pub fn to_geo_boundary(self) -> GeoBoundary {
let mut gb = GeoBoundaryInternal::new();
unsafe {
h3ToGeoBoundary(self.0, &mut gb);
}
gb.convert()
}
/// Returns the resolution of the index.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// let h = H3Index::new(0x850dab63fffffff).unwrap();
/// assert_eq!(h.resolution(), 5);
/// ```
pub fn resolution(self) -> i32 {
unsafe { h3GetResolution(self.0) }
}
/// Returns the base cell number of the index.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// let h = H3Index::new(0x850dab63fffffff).unwrap();
/// assert_eq!(h.base_cell(), 6);
/// ```
pub fn base_cell(self) -> i32 {
unsafe { h3GetBaseCell(self.0) }
}
/// Returns a `bool` indicating whether this index has a resolution with a Class
/// III orientation.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// assert!(H3Index::new(0x850dab63fffffff).unwrap().is_res_class_3());
/// ```
pub fn is_res_class_3(self) -> bool {
unsafe { h3IsResClassIII(self.0) != 0 }
}
/// Returns a `bool` indicating whether this index represents a pentagonal cell.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::H3Index;
///
/// assert!(H3Index::new(0x821c07fffffffff).unwrap().is_pentagon());
/// assert!(!H3Index::new(0x850dab63fffffff).unwrap().is_pentagon());
/// ```
pub fn is_pentagon(self) -> bool {
unsafe { h3IsPentagon(self.0) != 0 }
}
/// Returns the distance in grid cells between two indexes or an error if finding the
/// distance fails. Finding the distance can fail because the two indexes are not comparable
/// (different resolutions), too far apart, or are separated by pentagonal distortion.
///
/// # Example
///
/// ```
/// // TODO
/// ```
pub fn distance(self, other: Self) -> Result<i32, Error> {
let d;
unsafe {
d = h3Distance(self.0, other.0);
}
if d < 0 {
return Err(Error::IncompatibleIndexes {
left: self,
right: other,
});
}
Ok(d)
}
pub fn krings(self, index: i32) -> [H3Index; 6] {
let values: [*mut u64; 6] = array_init::array_init(|_| -> *mut u64 {
let val: *mut u64 = &mut u64::default();
val
});
unsafe {
kRing(self.0, index, values);
}
let h3s: [H3Index; 6] = array_init::array_init(|i| -> H3Index {
let val = H3Index(values[i] as u64);
println!("{}", val); // printing out the resulting H3Index value to study
val
});
h3s
}
/// Returns the parent (coarser) index containing h.
///
/// # Example
///
/// ```
/// // TODO
/// ```
pub fn parent(self, res: i32) -> Result<Self, Error> {
let h;
unsafe {
h = h3ToParent(self.0, res);
}
if h == 0 {
return Err(Error::FailedConversion);
}
Ok(Self(h))
}
}
impl fmt::Display for H3Index {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = vec![0u8; 17];
unsafe {
h3ToString(self.0, buf.as_mut_ptr() as *mut i8, buf.capacity());
}
let res = String::from_utf8(buf);
let s = res
.as_ref()
.map(|s| s.trim_end_matches('\0'))
.unwrap_or("<invalid>");
write!(f, "{}", s)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct GeoCoordInternal {
pub lat: f64,
pub lon: f64,
}
impl GeoCoordInternal {
pub fn new(lat: f64, lon: f64) -> Self {
Self { lat, lon }
}
fn to_deg(&self) -> GeoCoord {
GeoCoord::new(self.lat * RAD_TO_DEG, self.lon * RAD_TO_DEG)
}
fn to_h3(&self, res: i32) -> H3Index {
unsafe { H3Index(geoToH3(self, res)) }
}
}
/// GeoCoord is a point on the earth. It is comprised of a latitude and longitude expressed in
/// degrees. The C API for H3 expects the latitude and longitude to be expressed in radians so
/// the coordinates are transparently converted to radians before being passed to the C library.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct GeoCoord {
pub lat: f64,
pub lon: f64,
}
impl GeoCoord {
/// Creates a new `GeoCoord` from the given latitude and longitude. The unit of the
/// coordinates is degrees.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::GeoCoord;
///
/// let mut coord: GeoCoord = GeoCoord::new(67.194013596, 191.598258018);
/// ```
pub fn new(lat: f64, lon: f64) -> Self {
Self { lat, lon }
}
fn to_radians(&self) -> GeoCoordInternal {
GeoCoordInternal::new(self.lat * DEG_TO_RAD, self.lon * DEG_TO_RAD)
}
/// Indexes the location at the specified resolution.
///
/// # Example
///
/// ```
/// extern crate h3_rs as h3;
/// use h3::{GeoCoord, H3Index};
///
/// let mut coord: GeoCoord = GeoCoord::new(67.194013596, 191.598258018);
/// assert_eq!(coord.to_h3(5).unwrap(), H3Index::new(0x850dab63fffffff).unwrap());
/// ```
pub fn to_h3(&self, res: i32) -> Result<H3Index, Error> {
let index = self.to_radians().to_h3(res);
if index.0 == 0 {
return Err(Error::FailedConversion);
}
return Ok(index);
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct GeoBoundaryInternal {
num_verts: i32,
verts: [GeoCoordInternal; MAX_CELL_BNDRY_VERTS],
}
impl GeoBoundaryInternal {
fn new() -> Self {
Self {
num_verts: 0,
verts: [GeoCoordInternal::new(0.0, 0.0); MAX_CELL_BNDRY_VERTS],
}
}
fn convert(&self) -> GeoBoundary {
let mut verts = Vec::with_capacity(self.num_verts as usize);
for i in 0..self.num_verts {
verts.push(self.verts[i as usize].to_deg());
}
GeoBoundary { verts }
}
}
/// GeoBoundary is a collection of points which defines the boundary of a cell.
#[derive(Debug, Clone)]
pub struct GeoBoundary {
pub verts: Vec<GeoCoord>,
}
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "invalid value for H3 index: {}", value)]
InvalidIndex { value: u64 },
#[fail(display = "invalid string representation of H3 index: {}", value)]
InvalidString { value: String },
#[fail(display = "could not convert to H3 index")]
FailedConversion,
#[fail(display = "h3 indexes are incompatible: {} and {}", left, right)]
IncompatibleIndexes { left: H3Index, right: H3Index },
}
#[cfg(test)]
mod tests {
use super::*;
struct Setup {
valid_index: H3Index,
pentagon_index: H3Index,
valid_geo_coord: GeoCoord,
}
impl Setup {
fn new() -> Self {
Self {
valid_index: H3Index::new(0x850dab63fffffff).unwrap(),
pentagon_index: H3Index::new(0x821c07fffffffff).unwrap(),
valid_geo_coord: GeoCoord::new(67.15092686397712, -168.39088858096966),
}
}
}
#[test]
fn test_h3_from_str() {
assert_eq!(
H3Index::from_str("0x850dab63fffffff").unwrap(),
H3Index::new(0x850dab63fffffff).unwrap()
);
assert!(H3Index::from_str("invalid string").is_err());
}
#[test]
fn test_h3_to_geo() {
let setup = Setup::new();
assert_eq!(setup.valid_index.to_geo(), setup.valid_geo_coord);
}
#[test]
fn test_h3_to_geo_boundary() {
// TODO
}
#[test]
fn test_h3_resolution() {
let setup = Setup::new();
for res in 0..16 {
let h = setup.valid_geo_coord.to_h3(res).unwrap();
assert_eq!(h.resolution(), res);
}
}
#[test]
fn test_h3_base_cell() {
let setup = Setup::new();
assert_eq!(setup.valid_index.base_cell(), 6);
}
#[test]
fn test_h3_is_res_class_3() {
let setup = Setup::new();
assert!(setup.valid_index.is_res_class_3());
// TODO: Test an index which should return from false. From the Go package:
// res := Resolution(validH3Index) - 1
// parent := ToParent(validH3Index, res)
// assert.False(t, IsResClassIII(parent))
}
#[test]
fn test_h3_is_pentagon() {
let setup = Setup::new();
assert!(!setup.valid_index.is_pentagon());
assert!(setup.pentagon_index.is_pentagon());
}
#[test]
fn test_h3_distance() {
// let setup = Setup::new();
// TODO
}
#[test]
fn test_h3_parent() {
// let setup = Setup::new();
// TODO
}
#[test]
fn test_h3_display() {
let setup = Setup::new();
assert_eq!(format!("{}", setup.valid_index), "850dab63fffffff");
}
#[test]
fn test_geo_to_h3() {
let setup = Setup::new();
assert_eq!(setup.valid_geo_coord.to_h3(5).unwrap(), setup.valid_index);
assert!(setup.valid_geo_coord.to_h3(-1).is_err());
assert!(setup.valid_geo_coord.to_h3(17).is_err());
}
}
fn main() {
// geoToH3 --resolution 6 --latitude 43.6411124 --longitude -79.4180424
let _coord = GeoCoordInternal{ lat: 43.6411124, lon: -79.4180424 };
let index = _coord.to_h3(6); // 862b9bc57ffffff
// kRing -k 1 --origin 862b9bc57ffffff
let _first = index.krings(1);
// 862b9bc57ffffff
// 862b9bc0fffffff
// 862b9bce7ffffff
// 862b9bcefffffff
// 862b9bc5fffffff
// 862b9bc47ffffff
// 862b9bc77ffffff
println!("{}", index);
}
You can see I add the definition in the extern "C" block:
fn kRing(origin: c_ulonglong, k: c_int, h3: [ *mut c_ulonglong; 6 ]);
I figured [ *mut c_ulonglong; 6 ] is appropriate because the documentation at https://h3geo.org/#/documentation/api-reference/traversal has an interface of void kRing(H3Index origin, int k, H3Index* out); where out is a mutable array of H3Index.
Once the C function is declared I attempt using it:
pub fn krings(self, index: i32) -> [H3Index; 6] {
let values: [*mut u64; 6] = array_init::array_init(|_| -> *mut u64 {
let val: *mut u64 = &mut u64::default();
val
});
unsafe {
kRing(self.0, index, values);
}
let h3s: [H3Index; 6] = array_init::array_init(|i| -> H3Index {
let val = H3Index(values[i] as u64);
println!("{}", val);
val
});
h3s
}
In my main I then run through the process of grabbing an H3 index for some defined coordinates then grabbing the kring:
let _coord = GeoCoordInternal{ lat: 43.6411124, lon: -79.4180424 };
let index = _coord.to_h3(6);
The result of this should be 862b9bc57ffffff. Then I call my krings method with a k value of 1 (which should return 6 values).
The values I get are:
7ffeeace1ea8
7ffeeace1ea8
7ffeeace1ea8
7ffeeace1ea8
7ffeeace1ea8
7ffeeace1ea8
Which seems to be an incorrect return value. The correct values I'm looking for are:
862b9bc57ffffff
862b9bc0fffffff
862b9bce7ffffff
862b9bcefffffff
862b9bc5fffffff
862b9bc47ffffff
862b9bc77ffffff
I believe the incorrect values are just the index representations based on u64::default() indicating that the array isn't being populated.
I'm also getting unsafe FFI errors which I'm not sure how to handle and I believe are the reason why I'm having incorrect values returned.
Note that the logic above will inevitably change. The array size will have to be dynamic/unspecified because the size of the array returned by the C function changes based on the value of k (k * 6 + 1 to be precise).
mcarton had a lot of really great points. I found another libh3 that implemented these properly. Here's example of a properly implemented kring function:
pub fn kring(origin:H3Index, radius: i32) -> Vec<H3Index> {
unsafe {
let max = libh3_sys::maxKringSize(radius);
let mut r = Vec::<H3Index>::with_capacity(max as usize);
kRing(origin, radius, r.as_mut_ptr());
r.set_len(max as usize);
r = r.into_iter().filter(|v| *v != 0).collect();
return r;
}
}
Beyond mcarton's points using a Vector to store and return index values was necessary.
There are several problems with your code:
The first one is obviously the one reported by the warning: C does not have first class arrays, therefore you cannot pass Rust arrays to a C function. You need to use [T]::as_ptr to pass a pointer to your array to the C function.
{
let val: *mut u64 = &mut u64::default();
val
}
creates a pointer to a temporary value. Using this pointer is instant UB.
pub struct H3Index(u64); is not ABI compatible with u64 or c_ulonglong. You'd need to add #[repr(transparent)] to the structure declaration for that.
H3Index* is a pointer to an array of H3Index, but you are passing an array of pointer of H3Index to your function.
I have an struct with a refence to an array of type T:
pub struct myStruct<'a, T> {
pub data: &'a [T],
}
I want to modify one element of this array and check the result of an operation. for that I am trying to copy the array, modify the value and execute the operation:
pub fn check_value(&self, data: &T, position: usize) -> bool {
if position >= self.data.len() {
return false;
}
let array_temp = Box::new(self.data);
array_temp[position] = *data;
return mycheck(array_temp);
}
I am getting this error:
error[E0594]: cannot assign to `array_temp[_]` which is behind a `&` reference
I would like to know how to copy the array and modify the value or just modify directly the value in the original array (data) and restore the original value later.
Here you have a complete code to compile
pub struct MyStruct<'a, T> {
pub data: &'a [T],
}
impl<'a, T> MyStruct<'a, T>
where
T: Copy,
{
fn mycheck(&self, myarray: &[T]) -> bool {
if myarray.len() > 0 {
return true;
} else {
return false;
}
}
pub fn check_value(&self, data: &T, position: usize) -> bool {
if position >= self.data.len() {
return false;
}
let array_temp = Box::new(self.data);
array_temp[position] = *data;
return self.mycheck(&array_temp);
}
}
fn main() {
println!("Hello World!");
}
You do not have an array (whose length is known), but you have a slice (whose length is not known at compile time). Thus, you must adjust to the dynamic length.
You probably want to use self.data.to_vec() instead of Box::new(self.data).
to_vec copies the values into a newly allocated vector having enough capacity.
I have an external library (e.g. libcisland.so) with interface like this:
size_t lib_handle_size();
typedef void* handle;
int lib_init(handle h);
int lib_store(handle h, int value);
int lib_restore(handle h, int *pvalue);
The user of this library is expected to do following:
// allocate some buffer in client address space
handle h = malloc(lib_handle_size());
// pass this buffer to library for initialization
if (lib_init(h)) { /* handle errors */ }
// library initializes this handle by some opaque fashion
// then user uses it
lib_store(h,42);
int r;
lib_restore(h,&r);
// after all work is done, user frees this handle
free(h);
I can't figure out how to properly wrap this interface to Rust.
This is what I ended up:
pub struct Island {
h: Handle,
v: Vec<u8>,
}
impl Island {
pub fn new() -> Island {
let len = unsafe { lib_handle_size() };
let mut v: Vec<u8> = Vec::with_capacity(len);
let h: Handle = v.as_mut_ptr();
Island { v:v, h:h, }
}
pub fn store(&mut self, v: i32) {
unsafe { lib_store(self.h, v); }
}
pub fn restore(&mut self) -> i32 {
let mut v = 0;
unsafe { lib_restore(self.h, &mut v); }
v
}
}
impl Drop for Island {
fn drop(&mut self) {
drop(&mut self.v);
}
}
/// unsafe part
use libc::size_t;
pub type Handle = *mut u8;
#[link(name="cisland")]
extern {
pub fn lib_handle_size() -> size_t;
pub fn lib_init(h: Handle) -> i32;
pub fn lib_store(h: Handle, value: i32) -> i32;
pub fn lib_restore(h: Handle, pvalue: &mut i32) -> i32;
}
Is it Ok to use Vec(u8) for this purpose? Is this Drop trait implemented properly?
Is it Ok to use Vec(u8) for this purpose?
I think Vec<u8> is ok, but you should initialize it rather than using a zero-length vector, pointing at uninitialized memory. It would also be more robust to use Box<[u8]> because that will enforce that it can't be reallocated accidentally.
Is this Drop trait implemented properly?
It should not be necessary to implement Drop at all. The fields of Island each will drop correctly anyway.
Rather than store the handle, I would get it each time using a method. Then your struct is much simpler.
use libc::c_void;
pub struct Island {
buf: Box<[u8]>,
}
impl Island {
pub fn new() -> Island {
let len = unsafe { lib_handle_size() };
let v: Vec<u8> = vec![0; len];
Island { buf: v.into_boxed_slice() }
}
pub fn store(&mut self, v: i32) {
unsafe { lib_store(self.handle_mut(), v); }
}
pub fn restore(&mut self) -> i32 {
let mut v = 0;
unsafe { lib_restore(self.handle_mut(), &mut v); }
v
}
fn handle_mut(&mut self) -> *mut c_void {
self.buf.as_mut_ptr() as *mut c_void
}
}
You don't need a Drop implementation because the Box will drop automatically when it goes out of scope (as would a Vec).
Is it Ok to use Vec(u8) for this purpose?
A vector is not mean to be use like that, even if your code should work this is not a good method.
To do it properly you need an experimental feature (this one is quite stable), you need to use System structure and Alloc trait. Unfortunately, your library doesn't give any alignment requirement for its handle so we must use 1.
pub type Handle = *mut u8; is incorrect according to your typedef void* handle; (by the way hide pointer is bad). It should be pub type Handle = *mut libc::c_void;.
#![feature(allocator_api)]
use std::alloc::{Alloc, Layout, System};
use std::ptr::NonNull;
pub struct Island {
handle: NonNull<u8>,
layout: Layout,
}
impl Island {
pub fn new() -> Island {
let size = unsafe { lib_handle_size() };
let layout = Layout::from_size_align(size, 1).unwrap();
let handle = unsafe { System.alloc(layout).unwrap() };
unsafe {
// can have error I guess ?
lib_init(handle.as_ptr() as Handle);
}
Self { handle, layout }
}
pub fn store(&mut self, v: i32) -> Result<(), ()> {
unsafe {
lib_store(self.handle.as_ptr() as Handle, v);
}
Ok(())
}
pub fn restore(&mut self, v: &mut i32) -> Result<(), ()> {
unsafe {
lib_restore(self.handle.as_ptr() as Handle, v);
}
Ok(())
}
}
impl Drop for Island {
fn drop(&mut self) {
unsafe { System.dealloc(self.handle, self.layout) }
}
}
/// unsafe part
use libc::size_t;
pub type Handle = *mut libc::c_void;
#[link(name = "cisland")]
extern "C" {
pub fn lib_handle_size() -> size_t;
pub fn lib_init(h: Handle) -> i32;
pub fn lib_store(h: Handle, value: i32) -> i32;
pub fn lib_restore(h: Handle, pvalue: &mut i32) -> i32;
}
I change a little bit your store() and restore() function to return an Result. I bet your C function do the same.
I am trying to write a parser that passes an iterator over a vector to a function. The code is similar to this:
fn foo(itr : ???) {
while let Some(c) = itr.next() {
if *c != 0 {
break;
}
println!("{}", *c);
}
}
fn main() {
let v = vec![0; 10];
let itr = v.iter();
while let Some(c) = itr.next() {
foo(itr);
}
}
I am not sure how to write the type of the iterator over the vector. I tried putting the wrong type u32 to see what type rustc expected: core::slice::Iter<'_, _>. When I try to use core::slice rustc complains Use of undeclared type or module 'core::slice'
There are many types of iterators; most of the time, what you really want is a function that is able to consume any of them. To do this, the idiomatic solution is to use generics.
fn foo<'a, T: Iterator<Item=&'a i32>>(mut itr: T) {
while let Some(c) = itr.next() {
if *c != 0 {
break;
}
println!("{}", *c);
}
}
fn main() {
let v = vec![0; 10];
let mut itr = v.iter();
while let Some(c) = itr.next() {
foo(itr);
}
}
The code above doesn't compile though, since it moves itr into foo, then tries to use it again on the while let. To solve this, we need to pass the iterator by reference instead.
fn foo<'a, T: Iterator<Item=&'a i32>>(itr: &mut T) {
while let Some(c) = itr.next() {
if *c != 0 {
break;
}
println!("{}", *c);
}
}
fn main() {
let v = vec![0; 10];
let mut itr = v.iter();
while let Some(c) = itr.next() {
foo(&mut itr);
}
}
Instead of generics, we can also use a trait object:
fn foo<'a>(itr: &mut Iterator<Item=&'a i32>) {
while let Some(c) = itr.next() {
if *c != 0 {
break;
}
println!("{}", *c);
}
}
fn main() {
let v = vec![0; 10];
let mut itr = v.iter();
while let Some(c) = itr.next() {
foo(&mut itr);
}
}
The chapter on trait objects from the Rust book explains the difference between these solutions.
The solution was to add
use std::slice::Iter;
and the type was
fun foo<'a>(itr : &mut Iter<'a, i32>) {}
I'm playing with Nickel.rs to build a todo list example. As closures are not supported for the moment, I am trying to find another way deal with the simple structure I implemented.
Here is my code :
extern crate nickel;
use std::io::net::ip::Ipv4Addr;
use nickel::{Nickel, Request, Response};
struct TaskList {
list: Vec<String>
}
impl TaskList {
fn new() -> TaskList {
TaskList { list: Vec::new() }
}
fn add_task (&mut self, task: &str) {
&self.list.push(task.to_string());
}
fn get_tasks (&self) -> Vec<String> {
self.list.to_vec()
}
}
fn main() {
let mut server = Nickel::new();
static mut sample : TaskList = TaskList { list: Vec::new() };
sample.add_task("First");
sample.add_task("Second");
fn fetch_tasks (_request: &Request, response: &mut Response) {
response.send(sample.get_tasks().to_string())
}
server.utilize(Nickel::static_files("./public"));
server.get("/task", fetch_tasks);
server.listen(Ipv4Addr(127, 0, 0, 1), 6767);
}
But the compiler write me this : "mutable static items are not allowed to have destructors"
Do you have any advice on how I can solve this ?
I'm not really sure what you're trying to achieve.
If you want the TaskList to exist on the heap, use Box. However, stack scope should be valid event inside server.listen(), so I don't see why you'd need TaskList to be a static mut?
If you want to mess around with static variables, you have to do it unsafely, like this:
use std::mem::transmute;
use std::ptr;
struct Static {
v: int
}
impl Static {
fn whatever(&mut self) {
println!("Write to static");
self.v += 1;
}
}
static mut _data:*const Static = 0 as *const Static;
unsafe fn get<'a>() -> &'a mut Static {
if _data == ptr::null::<Static>() {
// Notice this is a Box<Static>, which is a *Static allocated on the heap
// transmute(Static { v: 0 }) wouldn't work because once the stack scope ends
// the instance would no longer be valid; Box<T> lasts beyond the call to get()
_data = transmute(box Static { v: 0 });
}
return transmute(_data);
}
unsafe fn release() {
ptr::read::<Static>(_data);
}
impl Drop for Static {
fn drop(&mut self) {
println!("Dropped static");
}
}
fn main() {
unsafe {
let foo = get();
foo.whatever();
}
unsafe {
let foo = get();
foo.whatever();
}
unsafe {
release();
}
println!("Done");
}
I really strongly recommend against it though, unless there's a very good reason.
Most of the time you can assume the a variable you create in one scope:
{
let foo = Bar;
...
} <-- End
Will continue to be valid until the end of that scope.
A sub call like server.get is still inside the main() { } scope where sample is defined.
It'll still be valid.