-
Notifications
You must be signed in to change notification settings - Fork 53
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
consider making Value optional #109
Comments
cc #4
|
The Value type is designed to prevent an accidental "cast" from one type to another when storing and then retrieving a value. I understand how Value might feel extraneous for a use case that only ever deals with Blob values. And I'm open to ideas for mitigating the overhead. However, I'm also wary of adding more complexity to the interface, and a parallel set of methods that take |
@mykmelez why not just this (a breaking change)? pub fn put<K: AsRef<[u8]>, V: AsRef<[u8]>>(self, txn: &mut RwTransaction, k: K, v: V) -> Result<(), StoreError>;
pub fn get<T: Transaction, K: AsRef<[u8]>>(self, txn: &T, k: K) -> Result<Option<&[u8]>, StoreError> |
pub fn put<'a, K: AsRef<[u8]>, V: Cow<'a, [u8]>>(self, txn: &mut RwTransaction, k: K, v: V) -> Result<(), StoreError>; EDIT: V: AsRef<[u8]> should work for And for a value the calls would be // serialization to vec in `to_bytes` can't fail, right?
store.put(&mut writer, key, value.to_bytes()?);
store.get(&reader, key).and_then(Value::parse);
// Expose a helper function to transform the result.
impl Value {
/// Tries to parse the value from bytes.
pub fn parse(value: Option<&[u8]>) -> Result<Option<Value>, StoreError> {
Ok(match value {
Some(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError)?,
None => None,
})
}
} and for bytes it would simply be store.put(&mut writer, key, bytes);
store.get(&reader, key); |
This feels harder than it should be to read and write typed values. I wonder if it would be sufficient to expose a new Store type ( |
First of all - rkv looks like a great project. I like the level of documentation. I was going to open an issue that was almost exactly the same as this one. For my use case, I'd always be using Blobs - and the necessity of always wrapping and unwrapping against that type is a bit against the rust mantra of "zero cost abstractions". My guess is that most use cases will always choose a single value type, and in the current implementation would always end up wrapping and unwrapping against that. I think it would be good to abstract a little bit, and pull the value type up to the db layer - allow the user to choose any generic value type they want (even |
@mykmelez What do you think of making store types generic over a value type? pub struct SingleStore<V: Value> {
/// ...,
phantom: std::marker::PhantomData<V>,
}
impl<V: Value> for SingleStore<V> {
pub fn get<T: Readable, K: AsRef<[u8]>>(
self,
reader: &T,
k: K
) -> Result<Option<V>, StoreError> {...}
pub fn put<K: AsRef<[u8]>>(
self,
writer: &mut Writer,
k: K,
v: V
) -> Result<(), StoreError> {...}
}
// FIXME: inherit from std::convert::TryFrom<Error=Error, [u8]> when it's stabilized
extern crate try_from;
use try_from::TryFrom;
pub trait Value<'a>: TryFrom<&'a [u8], Err = Error> + AsRef<[u8]> {}
impl Value for rkv::value::Value {
/// ...
} Note, that trait |
This would require structs that own a SingleStore to specify the lifetime of its
Which limits usage in Firefox, where rkv is being exposed to JavaScript code via an FFI that cannot wrap generic types. I wonder if it would be possible to make store types enums, with a variant that accepts the #[derive(Copy, Clone)]
pub enum SingleStore<V: ValueTrait> {
Value { db: Database, },
ValueTrait {
db: Database,
phantom: PhantomData<V>,
},
} Then, assuming one can implement That would still be ergonomically suboptimal, as consumers would have to specify the type of I wonder if the approach taken by the lmdb crate would be preferable: provide access to the wrapped type, so consumers who aren't satisfied by the higher-level type can access the lower-level type directly. For the lmdb crate, that means you can get an So you could create a |
I'm not sure it's possible to specify
This seems like a good approach to me. |
Is there anything stopping us from simply adding a Failing that, I like the |
One could conceivably implement I intended rkv to expose most of LMDB's capabilities with way, way fewer sharp edges, and introducing a rudimentary type system of tagged values was part of that. But I had also intended to facilitate storing an arbitrary set of bytes. I haven't thought it through in detail, but conceivably the |
Currently
Rkv
provides 4 types of Stores: Single, Multi, Integer and MultiInteger. All of them require the value to be of typeValue
, which imposes certain overhead, since the values must be encoded and decoded (and copied). This can be undesirable if the user only usesBlob
type for values.In general, it feels like this type (
Value
) and its encoding logic (or compression) should be specific for each user and I don't quite understand why it is here. I could use lmdb-rkv, but it would be nice not to deal with UNC paths on Windows and restrictions like one environment per process per path. Consider adding methods do deal with&[u8]
values instead ofValue
.The text was updated successfully, but these errors were encountered: