Documentation updates for new handle behavior, and some minor cleanup

This commit is contained in:
kyren 2018-03-12 17:50:48 -04:00
parent 4358034bbf
commit c6c90f201c
4 changed files with 34 additions and 32 deletions

View file

@ -6,20 +6,19 @@
[Guided Tour](examples/guided_tour.rs) [Guided Tour](examples/guided_tour.rs)
This library is a high level interface between Rust and Lua. Its major goal is This library is a high level interface between Rust and Lua. Its major goals
to expose as easy to use, practical, and flexible of an API between Rust and Lua are to expose as easy to use, practical, and flexible of an API between Rust and
as possible, while also being completely safe. Lua as possible, while also being *completely* safe.
`rlua` is designed around "registry handles" to values inside the Lua state. `rlua` is NOT designed to be a perfect zero cost wrapper over the Lua C API,
This means that when you get a type like `rlua::Table` or `rlua::Function` in because such a wrapper cannot maintain the safety guarantees that `rlua` is
Rust, what you actually hold is an integer key into the Lua registry. This is designed to have. Every place where the Lua C API may trigger an error longjmp
different from the bare Lua C API, where you create tables / functions on the in any way is protected by `lua_pcall`, and the user of the library is protected
Lua stack and must be aware of their stack location. This is also similar to from directly interacting with unsafe things like the Lua stack, and there is
how other Lua bindings systems like overhead associated with this safety. However, performance *is* a focus of the
[Selene](https://github.com/jeremyong/Selene) for C++ work, but it means that library to the extent possible while maintaining safety, so if you encounter
using `rlua` may be slightly slower than what you could conceivably write using something that egregiously worse than using the Lua C API directly, or simply
the C API. The reasons for this design are safety and flexibility, and to something you feel could perform better, feel free to file a bug report.
prevent the user of `rlua` from having to be aware of the Lua stack at all.
There are currently a few missing pieces of this API: There are currently a few missing pieces of this API:
@ -30,8 +29,7 @@ There are currently a few missing pieces of this API:
* "Context" or "Sandboxing" support. There should be the ability to set the * "Context" or "Sandboxing" support. There should be the ability to set the
`_ENV` upvalue of a loaded chunk to a table other than `_G`, so that you can `_ENV` upvalue of a loaded chunk to a table other than `_G`, so that you can
have different environments for different loaded chunks. have different environments for different loaded chunks.
* Benchmarks, and quantifying performance differences with what you would * Quantifying performance differences to direct use of the Lua C API.
might write in C.
Additionally, there are ways I would like to change this API, once support lands Additionally, there are ways I would like to change this API, once support lands
in rustc. For example: in rustc. For example:
@ -40,11 +38,6 @@ in rustc. For example:
by macro for tuples up to size 12, it would be great if this was replaced by macro for tuples up to size 12, it would be great if this was replaced
with real variadic generics when this is available in Rust. with real variadic generics when this is available in Rust.
It is also worth it to list some non-goals for the project:
* Be a perfect zero cost wrapper over the Lua C API
* Allow the user to do absolutely everything that the Lua C API might allow
## API stability ## API stability
This library is very much Work In Progress, so there is a some API churn. This library is very much Work In Progress, so there is a some API churn.
@ -68,11 +61,11 @@ there ARE several internal panics and even aborts in `rlua` source, but they
should not be possible to trigger, and if you trigger them this should be should not be possible to trigger, and if you trigger them this should be
considered a bug. considered a bug.
There are some caveats to the panic / abort guarantee, however: Caveats to the panic / abort guarantee:
* `rlua` reserves the right to panic on API usage errors. Currently, the only * `rlua` reserves the right to panic on API usage errors. Currently, the only
time this will happen is when passed a registry handle type from a different time this will happen is when passed a registry handle type from a different
main Lua state. Lua state.
* Currently, there are no memory or execution limits on scripts, so untrusted * Currently, there are no memory or execution limits on scripts, so untrusted
scripts can always at minimum infinite loop or allocate arbitrary amounts of scripts can always at minimum infinite loop or allocate arbitrary amounts of
memory. memory.

View file

@ -558,7 +558,7 @@ impl Lua {
/// [`create_registry_value`]: #method.create_registry_value /// [`create_registry_value`]: #method.create_registry_value
pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> { pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> {
unsafe { unsafe {
if !Arc::ptr_eq(&key.unref_list, &(*self.extra()).registry_unref_list) { if !self.owns_registry_value(key) {
return Err(Error::MismatchedRegistryKey); return Err(Error::MismatchedRegistryKey);
} }
@ -585,7 +585,7 @@ impl Lua {
/// [`expire_registry_values`]: #method.expire_registry_values /// [`expire_registry_values`]: #method.expire_registry_values
pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> { pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> {
unsafe { unsafe {
if !Arc::ptr_eq(&key.unref_list, &(*self.extra()).registry_unref_list) { if !self.owns_registry_value(&key) {
return Err(Error::MismatchedRegistryKey); return Err(Error::MismatchedRegistryKey);
} }
@ -606,8 +606,9 @@ impl Lua {
/// Remove any registry values whose `RegistryKey`s have all been dropped. /// Remove any registry values whose `RegistryKey`s have all been dropped.
/// ///
/// Unlike normal handle values, `RegistryKey`s cannot automatically clean up their registry /// Unlike normal handle values, `RegistryKey`s do not automatically remove themselves on Drop,
/// entries on Drop, but you can call this method to remove any unreachable registry values. /// but you can call this method to remove any unreachable registry values not manually removed
/// by `Lua::remove_registry_value`.
pub fn expire_registry_values(&self) { pub fn expire_registry_values(&self) {
unsafe { unsafe {
let unref_list = mem::replace( let unref_list = mem::replace(

View file

@ -21,17 +21,22 @@ pub(crate) type Callback<'lua, 'a> =
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
/// ///
/// This is a handle into a value stored inside the Lua registry, similar to the normal handle types /// This is a handle to a value stored inside the Lua registry. It is not directly usable like the
/// like `Table` or `Function`. The difference is that this handle does not require holding a /// `Table` or `Function` handle types, but it is much more flexible and can be used in many
/// reference to a parent `Lua` instance, and thus is managed differently. Though it is more /// situations where it is impossible to directly store a normal handle type. It is Send + Sync +
/// difficult to use than the normal handle types, it is Send + Sync + 'static, which means that it /// 'static, and can be used by *any* `Lua` instance as long as it is derived from the same
/// can be used in many situations where it would be impossible to store a regular handle value. /// underlying main state (such as one received in a Rust callback). It is not automatically
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`], and
/// instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
/// ///
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very /// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve. /// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use /// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
/// `UserData::set_user_value` / `UserData::get_user_value`, and instead of moving a RegistryKey /// `UserData::set_user_value` / `UserData::get_user_value`, and instead of moving a RegistryKey
/// into a callback, prefer `Lua::scope`. /// into a callback, prefer `Lua::scope`.
///
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
pub struct RegistryKey { pub struct RegistryKey {
pub(crate) registry_id: c_int, pub(crate) registry_id: c_int,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>, pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,

View file

@ -10,7 +10,10 @@ use thread::Thread;
use userdata::AnyUserData; use userdata::AnyUserData;
use lua::Lua; use lua::Lua;
/// A dynamically typed Lua value. /// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a
/// parameter in a Rust callback, and doing so will result in a panic.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Value<'lua> { pub enum Value<'lua> {
/// The Lua value `nil`. /// The Lua value `nil`.