Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FromLisp for Vec? #47

Open
fosskers opened this issue Jun 20, 2022 · 3 comments
Open

FromLisp for Vec? #47

fosskers opened this issue Jun 20, 2022 · 3 comments

Comments

@fosskers
Copy link

fosskers commented Jun 20, 2022

Hi there, this is a really cool library and I'm excited to speed up my Emacs with Rust.

I'm having trouble figuring out how to send Elisp lists to my Rust functions. Something like this doesn't seem to compile:

#[emacs::defun]
fn mean(items: Vec<f64>) -> emacs::Result<f64> { ... }

as it complains that Vec is missing a FromLisp instance. I see that Elisp Vectors are supported, but lists aren't the same. Am I missing something really basic?

Please and thanks.

@fosskers
Copy link
Author

In the meantime, I'm making due with:

/// An iterator over what is assumed to be an underlying Elisp List.
struct List<'a> {
    items: Value<'a>,
}

impl<'a> Iterator for List<'a> {
    type Item = Value<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.items.car(), self.items.cdr()) {
            (Ok(v), Ok(next)) if self.items.is_not_nil() => {
                self.items = next;
                Some(v)
            }
            _ => None,
        }
    }
}

@justinbarclay
Copy link

justinbarclay commented Mar 30, 2024

I was working on this too recently, and I like your iterator option, but sometimes I just want to use into() to get a vec, so I took everything a step further

// An iterator over what is assumed to be an underlying Elisp List.
struct List<'a>(Value<'a>);

// This is a custom implementation of the FromLisp trait for the List type
// So that we can _error_ out safely when going from a Value to a List
impl<'a> FromLisp<'a> for List<'a> {
  fn from_lisp(value: Value<'a>) -> Result<Self> {
    let env = value.env;
    if env.call("listp", [value]).unwrap().is_not_nil() {
      Ok(List(value))
    } else {
      env.signal(env.intern("wrong-type-argument")?, [value, env.intern("list")?])
    }
  }
}

impl<'a> Iterator for List<'a> {
    type Item = Value<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.0.car(), self.0.cdr()) {
            (Ok(v), Ok(next)) if self.0.is_not_nil() => {
                self.0 = next;
                Some(v)
            }
            _ => None,
        }
    }
}

// We could call this directly on the Value type or our wrapper for the value type
// but then we would regularly expect panics here when a non list item is passed in
impl<'e, T: FromLisp<'e>> Into<Vec<T>> for List<'e>{
  fn into(self) -> Vec<T> {
    let mut result: Vec<T> = Vec::new();
    let mut list = self.0;

    while list.is_not_nil() {
      let car = list.car().unwrap();
      result.push(T::from_lisp(car).unwrap());
      list = list.cdr().unwrap();
    }
    result
  }
}

@justinbarclay
Copy link

justinbarclay commented Mar 30, 2024

@ubolonton I am not sure if you're open to expanding the interface, but I would love to see a system like above to allow converting from a Value to a Vec

If we could inline the above, then converting to a Vec<String> is comes out to:

        options.string_delimiters = List::from_lisp(new_value)?.into();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants