Ergonomic wrapper for FFI dictionary access with FFI defined string for key

I'm working on a wrapper around a sys crate and I'm having a little trouble with the ergonomics of a wrapped dictionary.

The sys crate implements its own symbol table (created from const char *) and uses those symbols as keys for dictionary access. I've wrapped the crate's Symbol with fallible TryInto implementations for String and &str and now I'm looking into wrapping the dictionary and stumbling with the ergonomics of the symbol creation.

Basically, I would really love to be able to do dict.insert(&"foo", 2); as well as
let s = Symbol::new("bar"); dict.insert(&s, 2084); but as is I'm doing dict.insert("foo".try_into().unwrap(), 2); insert's key is a Into<Symbol>

Basically, my question is.. would it make sense to just make the conversion from &str infallible, panicking on error, in sake of ergonomics, or, does anyone else have any better solution?

I guess one option could be to make the wrapper's Dict use a new struct, DictKey(Symbol), for its key and then make infallible (panicking) conversions from &str, Symbol, etc to DictKey ?

  • You could make the key type impl TryInto<Symbol> and either unwrap/expect and panic, or return Result.
  • You could make the calling code shorter by using a the ? operator, or creating a helper function or method.
  • If there is a limited set of possible symbols, you could use an enum, or a bunch of consts or statics.

In terms of panic! vs Result, how likely is the calling code going to be able to deal with the error in a way other than panicking?

1 Like

thanks @sollyucko , great ideas there.
impl TryInto<Symbol> could be it! Most of these methods return Result already so I could just add something like KeyError ..

Re calling code dealing with an error other than panicking, I'm not sure, this is library code for implementing a plugin, I hope that people implementing plugins don't panic but I also don't expect that they use keys that should cause a panic in the first place.

1 Like

If .insert() already yields a Result, then for all means shuffle the error handling of key conversion in there as well; but do note that you should be able to feature unfallible conversions if they keys are CStr literals or CStrings, at which point your question becomes is it possible to have ergonomic creation of those.

  • To that question, there are a bunch of crates out there which feature the possibility to create CStr "literals" / [almost] at compile time, such as zstr! for an actual &'static CStr, or c! for a char_p::Ref<'static>, which is the "same" except for it being a slim pointer already:

    use ::some_crate::… c;
    
    dict.insert(c!("foo"), 2);
    dict.insert(c!("f\0o"), 2); // <- Compile error
    

    Note that this can be done in addition to the impl TryInto<Symbol> API, so that people that don't know about c! or who have runtime-generated keys can still do their stuff, but be susceptible to invalid-key-related runtime failures, whereas the other cases can guarantee not hitting that runtime failure altogether :slight_smile:

3 Likes

thanks for pointing out these crates @Yandros !
I do already have infallible CString and &CStr conversions, so this is good to know!

1 Like

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.