# Try Create
[](https://crates.io/crates/try_create) <!-- Replace with your actual crate name if different -->
[](https://docs.rs/try_create) <!-- Replace with your actual crate name if different -->
`try_create` is a small Rust utility library that provides generic traits for object creation, offering standardized ways to handle both fallible and infallible construction.
## Overview
This library introduces a set of traits to streamline the creation of new type instances:
* **`TryNew`**: For fallible construction, where creating an instance might fail (e.g., due to validation rules). It returns a `Result`.
* **`New`**: For infallible construction. If an invariant is violated, this method is expected to panic.
* **`ConditionallyCreate`**: A utility trait that switches creation logic based on the build profile:
* In **debug** mode, it uses `TryNew::try_new().expect()`, panicking if `try_new` fails.
* In **release** mode, it uses `New::new()`.
* **`IntoInner`**: A re-exported trait from the [`into_inner`](https://crates.io/crates/into_inner) crate, used as a supertrait to define the input type for construction.
These traits are designed to be general-purpose and can be used for various types, promoting a consistent API for object instantiation. The library supports `no_std` environments.
## Installation
Add `try_create` to your `Cargo.toml`:
```toml
[dependencies]
try_create = "0.1.0" # Replace with the latest version
into_inner = "0.1.0" # try_create re-exports IntoInner, but you might depend on it directly too
```
## Usage
### 1. `IntoInner` Trait
Both `TryNew` and `New` require `IntoInner` to be implemented. This trait defines the `InnerType` that your constructor will accept and a way to retrieve this inner value from an instance.
```rust
use try_create::IntoInner;
#[derive(Debug, PartialEq)]
struct MyValueContainer {
value: i32,
}
impl IntoInner for MyValueContainer {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType {
self.value
}
}
```
### 2. `TryNew` Trait (Fallible Creation)
Use `TryNew` when the creation process can fail and you want to return a `Result`.
```rust
use try_create::{TryNew, IntoInner};
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;
// Define a custom error type
#[derive(Debug, PartialEq)]
struct NotPositiveError;
# #[cfg(feature = "std")]
impl fmt::Display for NotPositiveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Value must be positive")
}
}
# #[cfg(feature = "std")]
impl Error for NotPositiveError {}
// For no_std, NotPositiveError would just need to implement core::fmt::Debug,
// which it does via #[derive(Debug, PartialEq)].
// A struct that wraps an i32, ensuring it's positive.
#[derive(Debug, PartialEq)]
struct PositiveInteger {
value: i32,
}
impl IntoInner for PositiveInteger {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType { self.value }
}
impl TryNew for PositiveInteger {
type Error = NotPositiveError;
// `InnerType` is `i32`, inherited from `IntoInner`.
fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
if value > 0 {
Ok(PositiveInteger { value })
} else {
Err(NotPositiveError)
}
}
}
// Usage
assert_eq!(PositiveInteger::try_new(10), Ok(PositiveInteger { value: 10 }));
assert_eq!(PositiveInteger::try_new(0), Err(NotPositiveError));
assert_eq!(PositiveInteger::try_new(-5), Err(NotPositiveError));
let positive_num = PositiveInteger::try_new(42).unwrap();
assert_eq!(positive_num.into_inner(), 42);
```
### 3. `New` Trait (Infallible/Panicking Creation)
Use `New` when the creation process should not fail in a recoverable way. If invariants are violated, `New::new` should panic.
```rust
use try_create::{New, IntoInner, TryNew}; // TryNew often used to implement New
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;
// Example struct
#[derive(Debug, PartialEq)]
struct MyType(i32);
// Custom error for TryNew implementation (if used to implement New)
#[derive(Debug, PartialEq)]
struct MyTypeError(String);
# #[cfg(feature = "std")]
impl fmt::Display for MyTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MyTypeError: {}", self.0)
}
}
# #[cfg(feature = "std")]
impl Error for MyTypeError {}
impl IntoInner for MyType {
type InnerType = i32;
fn into_inner(self) -> Self::InnerType { self.0 }
}
// Optional TryNew, can be used to implement New
impl TryNew for MyType {
type Error = MyTypeError;
fn try_new(value: i32) -> Result<Self, Self::Error> {
if value < 0 { Err(MyTypeError("Value cannot be negative".to_string())) }
else { Ok(MyType(value)) }
}
}
impl New for MyType {
fn new(value: i32) -> Self {
// Example: using try_new and panicking on error
// match Self::try_new(value) {
// Ok(instance) => instance,
// Err(e) => panic!("MyType::new failed: {:?}", e),
// }
// Or direct implementation:
if value < 0 {
panic!("MyType::new: Value cannot be negative");
}
MyType(value)
}
}
// Usage
assert_eq!(MyType::new(10), MyType(10));
// The following would panic:
// MyType::new(-5);
```
### 4. `ConditionallyCreate` Trait
This trait provides a `create_conditionally` method that behaves differently in debug and release builds.
```rust
use try_create::{ConditionallyCreate, TryNew, New, IntoInner};
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;
// Using PositiveInteger and NotPositiveError from the TryNew example above
# #[derive(Debug, PartialEq)]
# struct NotPositiveError;
# #[cfg(feature = "std")]
# impl fmt::Display for NotPositiveError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Value must be positive") } }
# #[cfg(feature = "std")]
# impl Error for NotPositiveError {}
# #[derive(Debug, PartialEq)]
# struct PositiveInteger { value: i32 }
# impl IntoInner for PositiveInteger { type InnerType = i32; fn into_inner(self) -> Self::InnerType { self.value } }
# impl TryNew for PositiveInteger {
# type Error = NotPositiveError;
# fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
# if value > 0 { Ok(PositiveInteger { value }) } else { Err(NotPositiveError) }
# }
# }
// PositiveInteger must also implement New to use ConditionallyCreate
impl New for PositiveInteger {
fn new(value: Self::InnerType) -> Self {
match Self::try_new(value) {
Ok(instance) => instance,
Err(e) => panic!("PositiveInteger::new failed for a non-positive value. Error: {:?}", e),
}
}
}
// Usage of ConditionallyCreate
let p1 = PositiveInteger::create_conditionally(10);
assert_eq!(p1, PositiveInteger { value: 10 });
// If PositiveInteger::create_conditionally(-5) was called:
// - In debug mode: it would panic with "ConditionallyCreate: try_new() failed in debug mode".
// - In release mode: it would panic with "PositiveInteger::new failed...".
```
## `no_std` Support
The library is `no_std` compatible.
When the `std` feature is not enabled (default for `no_std` environments):
* `TryNew::Error` only requires `core::fmt::Debug`.
* You are responsible for defining error types that conform to this.
## Contributing
Contributions, issues, and feature requests are welcome!
## License
This project is licensed under the terms of the MIT license and the Apache License (Version 2.0). See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details. (You'll need to add these license files to your project).