Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ keywords = ["trait", "cast", "any"]
include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"]

[dependencies]
once_cell = "1.4"
hashbrown = { version = "0.17", default-features = false }
spin = { version = "0.10", default-features = false, features = ["lazy"] }
linkme = "0.2"
intertrait-macros = { version = "=0.2.2", path = "macros" }

Expand Down
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

This library provides direct casting among trait objects implemented by a type.

`intertrait` supports both `std` and `no_std + alloc` environments. The runtime crate itself is
`#![no_std]`, but it requires `alloc` because it uses `Box`, `Rc`, `Arc`, and a heap-backed registry.

In Rust, a trait object for a sub-trait of [`std::any::Any`] can be downcast to a concrete type at runtime
if the type is known. But no direct casting between two trait objects (i.e. without involving the concrete type
of the backing value) is possible (even no coercion from a trait object for a trait to that for its super-trait yet).
Expand All @@ -25,6 +28,27 @@ linkme = "0.2"

The `linkme` dependency is required due to the use of `linkme` macro in the output of `intertrait` macros.

## `no_std` Support
`intertrait` can be used in `no_std` targets as long as `alloc` is available. No feature toggle is needed.

This means:

* `intertrait` works directly in `std` environments.
* `intertrait` also works in `no_std` environments with `extern crate alloc`.
* This is not a pure-`core` crate, because casting support relies on allocation-backed types such as `Box`,
`Rc`, and `Arc`.

Minimal `no_std` usage looks like this:

```rust,ignore
#![no_std]

extern crate alloc;

use intertrait::*;
use intertrait::cast::*;
```

# Usage

```rust
Expand Down Expand Up @@ -116,7 +140,7 @@ fn main() {}
```

## `Arc` Support
`std::sync::Arc` is unique in that it implements `downcast` method only on `dyn Any + Send + Sync + 'static'.
`Arc` is unique in that it implements `downcast` method only on `dyn Any + Send + Sync + 'static`.
To use with `Arc`, the following steps should be taken:

* Mark source traits with [`CastFromSync`] instead of [`CastFrom`]
Expand Down Expand Up @@ -164,4 +188,4 @@ dual licensed as above, without any additional terms or conditions.
[`std::any::Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
[`TypeId`]: https://doc.rust-lang.org/std/any/struct.TypeId.html
[`CastFrom`]: https://docs.rs/intertrait/*/intertrait/trait.CastFrom.html
[`CastFromSync`]: https://docs.rs/intertrait/*/intertrait/trait.CastFromSync.html
[`CastFromSync`]: https://docs.rs/intertrait/*/intertrait/trait.CastFromSync.html
6 changes: 3 additions & 3 deletions macros/src/gen_caster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) -
};

quote! {
#[::linkme::distributed_slice(::intertrait::CASTERS)]
fn #fn_ident() -> (::std::any::TypeId, ::intertrait::BoxedCaster) {
(::std::any::TypeId::of::<#ty>(), Box::new(#new_caster))
#[::intertrait::__private::linkme::distributed_slice(::intertrait::CASTERS)]
fn #fn_ident() -> (::core::any::TypeId, ::intertrait::BoxedCaster) {
(::core::any::TypeId::of::<#ty>(), ::intertrait::__private::Box::new(#new_caster))
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/cast/cast_arc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::sync::Arc;

use crate::{caster, CastFromSync};
use std::sync::Arc;

/// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting
/// of a trait object for it behind an `Rc` to a trait object for another trait
Expand Down
2 changes: 2 additions & 0 deletions src/cast/cast_box.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::boxed::Box;

use crate::{caster, CastFrom};

/// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting
Expand Down
3 changes: 2 additions & 1 deletion src/cast/cast_rc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::rc::Rc;

use crate::{caster, CastFrom};
use std::rc::Rc;

/// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting
/// of a trait object for it behind an `Rc` to a trait object for another trait
Expand Down
2 changes: 1 addition & 1 deletion src/cast/cast_ref.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::any::TypeId;
use core::any::TypeId;

use crate::{caster, CastFrom, Caster, CASTER_MAP};

Expand Down
6 changes: 3 additions & 3 deletions src/hasher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::convert::TryInto;
use std::hash::{BuildHasherDefault, Hasher};
use std::mem::size_of;
use core::convert::TryInto;
use core::hash::{BuildHasherDefault, Hasher};
use core::mem::size_of;

/// A simple `Hasher` implementation tuned for performance.
#[derive(Default)]
Expand Down
27 changes: 20 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![no_std]
//! A library providing direct casting among trait objects implemented by a type.
//!
//! In Rust, an object of a sub-trait of [`Any`] can be downcast to a concrete type
Expand Down Expand Up @@ -54,15 +55,18 @@
//! [`CastFrom`]: ./trait.CastFrom.html
//! [`CastFromSync`]: ./trait.CastFromSync.html
//! [`cast`]: ./cast/index.html
//! [`Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
//! [`Any`]: https://doc.rust-lang.org/core/any/trait.Any.html
//! [`Arc`]: https://doc.rust-lang.org/alloc/sync/struct.Arc.html
extern crate alloc;

use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::any::{Any, TypeId};

use hashbrown::HashMap;
use linkme::distributed_slice;
use once_cell::sync::Lazy;
use spin::Lazy;

pub use intertrait_macros::*;

Expand All @@ -74,6 +78,12 @@ mod hasher;
#[doc(hidden)]
pub type BoxedCaster = Box<dyn Any + Send + Sync>;

#[doc(hidden)]
pub mod __private {
pub use alloc::boxed::Box;
pub use linkme;
}

#[cfg(doctest)]
doc_comment::doctest!("../README.md");

Expand Down Expand Up @@ -282,6 +292,9 @@ impl CastFromSync for dyn Any + Sync + Send + 'static {
}
}

#[cfg(test)]
extern crate std;

#[cfg(test)]
mod tests {
use std::any::{Any, TypeId};
Expand Down