Some guidance on implementing a "map like" method, "tojson()" that takes and returns an iterator

I want to be able to do something like this.

  1. Take a C pointer, iterator over the characters, map the charactors to json which might mean quoting them string and escaping charactors.
  2. Implement a printfunction that takes an Iterator that returns u8 and print the charactors, that can take (1) above.
  3. Be able to collect the values from iterator and collect into a CString or
let cstr = CStr::from_bytes_with_nul(b"hello world\0");
let cstrptr = cstr.as_ptr();
printcstrptrfromiterator(CStrPtr::new(cstrptr).iter().tojson());
let X  = CStrPtr::new(cstrptr).iter().tojson().collect();   # returns bytearray or CString

I currently have a CStrPtr struct that implements an Iterator trait.

struct CStrPtr {
    cstrptr: *const u8;
    cptr: *const u8;
}

impl CStrPtr {
    pub fn new(p: *const u8) -> CStrPtr {
        CStrPtr { sptr: p, cptr: p}
    }
}

impl Iterator for CStrPtr {
    type Item = u8;
    
    fn next(&mut self) -> Option<u8> {
        let p =  unsafe { p.add(1) };
        if *p == 0 {
            None
        } else {
            Some(*p as u8)
        }
    }
}

I think what I have above gives me an iterator of the u8 in the C string pointer.
I need to implement tojson() that takes the iterator and maps the characters and possibly insert/return additional characters that turn the C String to a quoted json string.
And then collect the characters to return a CString or buffer/array.

I can't find good examples of how to do this.

  1. I suspect tojson() would be a like the map() method.
  2. Implement collect() that returns an array/CString.

You don't implement collect(); it is implemented automatically for FromIterator types.

Other than that, you could just use map, but it's not in general possible to return single characters mixed with escaped multi-character strings. You could use an enum:

#[derive(Clone, Copy, Debug)]
enum Character {
    Verbatim(u8),
    Escaped(&'static str),
}

fn json_escape_chars(string: &CStr) -> impl Iterator<Item=Character> {
    string.to_bytes().map(|&byte| {
        match byte {
            b'\n' => Character::Escaped("\\n"),
            b'\r' => Character::Escaped("\\r"),
            b'\t' => Character::Escaped("\\t"),
            b'"' => Character::Escaped("\\\""),
            b'\\' => Character::Escaped("\\\\"),
            _ => Character::Verbatim(byte),
        })
    }
}

Handling non-ASCII characters (those beyond 127) is left as an exercise.

Actually I am trying to implement a "Map" on an iterator, or function that takes an iterator and returns another iterator or modifies the iterator, to modify aka insert additional characters or modify existing characters as needed.

I am trying to figure out how to do that in Rust.

Filter takes and iterator and returns an iterator that skips some values, I guess I am trying to add, modify or delete elements from the iterator.

Your code seems interesting. Its almost like a python "yield i".

I didnt realize RUST supported python yield style functionality.

Could you point me to RUST documentation, that explains how the above code works and returns an iterator.

You don't have to re-implement map. You can just use the existing Iterator::map() implementation with whatever mapping function you provide to it. That's the point of it being generic over the function.

It doesn't. I just screwed up the syntax because I switched strategies mid-coding. It now correctly uses map.

Isnt there no way to take an Iterator and "map like function" it into another Iterator, where the map like function, may add, modify or delete u8 from the input iterator and returns Iterator as well?

Sure, you create a struct to hold your state and then implement Iterator, perhaps with some conversion utilities, maybe an IntoIterator implementation as well, etc.

My function above does just that except for the deletion. But nothing prevents you from writing e.g.

fn filter_and_map<I, F, M, U>(iter: I, predicate: F, projection: M) -> impl Iterator<Item=U>
    where I: IntoIterator,
          F: FnMut(&I::Item) -> bool,
          M: FnMut(I::Item) -> U
{
    iter.into_iter().filter(predicate).map(projection)
}

I may be misunderstanding what you want here, but this sounds like the sort of thing Iterator::flat_map() does, so that might be what you want.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.