#[Singleton]Expand description
Define a singleton provider.
#[Singleton], #[Transient] and #[SingleOwner] are attribute macros used to define a Provider, for the difference between the Providers they defined, see Scope.
These three macros can be used on struct, enum, impl block, and fn.
-
When used on
struct,enumandimpl block, aDefaultProviderimplementation is generated for thestructorenum, and the associated typeDefaultProvider::Typeis thestructorenumitself. -
When used on
fn, a struct with the same name as the function is created, and then aDefaultProviderimplementation is generated for the struct, with the associated typeDefaultProvider::Typebeing the return type of the function. As mentioned above, it is recommended to useCamelCasewhen defining functions. Of course, you can still usesnake_case.
§Example
use rudi::{Context, Singleton, Transient};
// impl block
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct A;
#[Singleton]
impl A {
#[di]
fn new() -> Self {
Self
}
}
// struct
#[Singleton]
#[derive(Debug, Clone, PartialEq, Eq)]
struct B(A);
// enum
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
#[Transient]
enum C {
One,
Two(A),
#[di]
Three {
b: B,
},
}
// fn
#[Singleton]
fn Run(a: A, b: B, c: C) {
assert_eq!(b, B(a));
assert_eq!(c, C::Three { b: B(a) });
}
fn main() {
let mut cx = Context::auto_register();
cx.resolve()
}§Attribute arguments
§#[Singleton] / #[Transient] / #[SingleOwner]: used on struct, enum, impl block and fn
§Common arguments that can be used on struct, enum, impl block, and fn
-
name
- type: any expression that implements
Into<Cow<'static, str>>. - example:
#[Singleton(name = "abc")]/#[Transient(name = a::b::NAME)]/#[SingleOwner(name = nth(42))] - optional: true
- default: “”
- description: Specifies the name of the defined
Provider. - refer:
- type: any expression that implements
-
eager_create
- type: bool
- example:
#[Singleton(eager_create)]/#[Transient(eager_create = true)]/#[SingleOwner(eager_create = false)] - optional: true
- default: false
- description: Specifies whether the defined
Provideris eager to create. - refer:
-
condition
- type: a closure or an expression path of type
fn(&Context) -> bool. - example:
#[Singleton(condition = |_cx| true)]/#[SingleOwner(condition = path::to::expr)] - optional: true
- default: None
- description: Specifies whether or not to insert the defined
Providerinto theContextbased on the condition. - refer:
- type: a closure or an expression path of type
-
binds
- type: array of paths to functions of type
fn(T) -> R, whereTis current struct type or current function return type andRcan be any type. - example:
#[Singleton(binds = [Rc::new, Box::new])] - optional: true
- default: Vec::new()
- description: Specifies the field
binding_providersandbinding_definitionsof the definedProvider. - refer:
- type: array of paths to functions of type
-
auto_register
- available only when the
auto-registerfeature flag is enabled - type: bool
- example:
#[Singleton(auto_register)]/#[Transient(auto_register = true)]/#[SingleOwner(auto_register = false)] - optional: true
- default: true
- description: Specifies whether a defined
Providershould be auto-registered toAutoRegisterModule. When theauto-registerfeature is enabled (which is enabled by default), this argument can be used if auto-registration is not desired, or if auto-registration is not possible due to the presence of generics.
- available only when the
§An argument that can only be used on struct and enum
- async
- type: bool
- example:
#[Singleton(async)] - optional: true
- default: false
- description: Specifies whether the constructor method of a defined
Provideris asynchronous. Only valid when used onstructandenum, forimpl blockandfncases useasync fn.
§#[di]: used on struct, enum, impl block and fn
- rudi_path
- type: path to the
rudicrate. - example:
#[di(rudi_path = path::to::rudi)] - optional: true
- default: ::rudi
- description: Specifies the path to the
rudicrate. This argument is used when therudicrate is not in the root of the crate.
- type: path to the
§#[di]: used on fn of impl block
Use #[di] to specify which function of the impl block will be called to construct an instance.
§#[di]: used on variant of enum
Use #[di] to specify which variant of the enum will be constructed.
§#[di]: used on field of struct, field of variant of enum and argument of function
-
name
- conflict:
vec - type: any expression that implements
Into<Cow<'static, str>>. - example:
#[di(name = "abc")]/#[di(name = a::b::NAME)]/#[di(name = nth(42))] - optional: true
- default: “”
- description: Specifies the name of the dependency to be taken out of
Context. - refer:
- conflict:
-
option
-
conflict:
default,vec -
require: The current
fieldorargument, which must be of typeOption<T>. -
type: bool.
-
example:
#[di(option)] -
optional: true
-
default: false
-
description:
From the call to the following method
cx.resolve_with_name::<Option<T>>(name)cx.resolve_with_name_async::<Option<T>>(name).await
Instead, call the following method
cx.resolve_option_with_name::<T>(name)cx.resolve_option_with_name_async::<T>(name).await
-
refer:
-
-
default
-
conflict:
option,vec -
require: If no default value is specified, the current
fieldorargumentmust implement theDefaulttrait. -
type: empty, or an arbitrary expression type.
-
example:
#[di(default)]/#[di(default = 42)]/#[di(default = a::b::func())] -
optional: true
-
default: None
-
description:
From the call to the following method
cx.resolve_with_name(name)cx.resolve_with_name_async(name).await
Instead, call the following method
match cx.resolve_option_with_name(name) { ... }match cx.resolve_option_with_name_async(name).await { ... }
-
refer:
-
-
vec
-
conflict:
name,option,default -
require: The current
fieldorargument, which must be of typeVec<T>. -
type: bool.
-
example:
#[di(vec)] -
optional: true
-
default: false
-
description:
From the call to the following method
cx.resolve_with_name::<Vec<T>>(name)cx.resolve_with_name_async::<Vec<T>>(name).await
Instead, call the following method
cx.resolve_by_type::<T>()cx.resolve_by_type_async::<T>()
-
refer:
-
-
ref
-
require:
- exist
optionargument: The currentfieldorargument, which must be of typeOption<&T>. - exist
vecargument: The currentfieldorargument, which must be of typeVec<&T>. - exist
defaultargument or not, the currentfieldorargument, which must be of type&T. - if using a type alias, specify the original type using
#[di(ref = T)], whereTis a non-reference type.
- exist
-
type:
Option<Type> -
example:
#[di(ref)]#[di(ref = i32)]#[di(option, ref)]#[di(option, ref = i32)]#[di(vec, ref)]#[di(vec, ref = i32)]#[di(default, ref)]#[di(default, ref = i32)]#[di(default = &42, ref)]#[di(default = &42, ref = i32)]
-
optional: true
-
default: None
-
description:
Get a reference to
SingletonorSingleOwnerfromContext.-
Not exist
option,vecanddefaultargument, will call the following methodⓘ// async cx.just_create_single_with_name_async::<T>(name).await; let var = cx.get_single_with_name(name); // sync cx.just_create_single_with_name::<T>(name); let var = cx.get_single_with_name(name); -
Exist
optionargument, will call the following methodⓘ// async cx.try_just_create_single_with_name_async::<T>(name).await; let var = cx.get_single_option_with_name(name); // sync cx.try_just_create_single_with_name::<T>(name); let var = cx.get_single_option_with_name(name); -
Exist
vecargument, will call the following methodⓘ// async cx.try_just_create_singles_by_type_async::<T>().await; let var = cx.get_singles_by_type(); // sync cx.try_just_create_singles_by_type::<T>(); let var = cx.get_singles_by_type(); -
Exist
defaultargument, will call the following methodⓘ// async cx.try_just_create_single_with_name_async::<T>(name).await; let var = match cx.get_single_option_with_name(name) { Some(value) => value, None => default, }; // sync cx.try_just_create_single_with_name::<T>(name); let var = match cx.get_single_option_with_name(name) { Some(value) => value, None => default, }; -
If specified using
#[di(ref = R)], then all of the aboveTs will be replaced with the specified typeR.
-
-
refer:
Context::just_create_single_with_name_asyncContext::just_create_single_with_nameContext::try_just_create_single_with_name_asyncContext::try_just_create_single_with_nameContext::try_just_create_singles_by_type_asyncContext::try_just_create_singles_by_typeContext::get_single_with_nameContext::get_single_option_with_nameContext::get_singles_by_type
-
§Struct, enum and function attributes example
use std::{fmt::Debug, rc::Rc};
use rudi::{Context, Singleton, Transient};
// name
#[Transient]
fn One() -> i8 {
1
}
#[Transient(name = "2")]
fn Two() -> i8 {
2
}
// eager_create
#[Singleton(name = "3")]
fn Three() -> i16 {
3
}
#[Singleton(name = "4", eager_create)]
fn Four() -> i16 {
4
}
// condition
fn _5_condition(cx: &Context) -> bool {
!cx.contains_single_with_name::<i32>("5")
}
#[Singleton(name = "5", condition = _5_condition)]
fn Five() -> i32 {
5
}
#[Singleton(name = "6", condition = |_cx| false)]
fn Six() -> i32 {
6
}
// binds
fn transform<T: Debug + 'static>(t: T) -> Rc<dyn Debug> {
Rc::new(t)
}
#[Singleton(name = "7")]
fn Seven() -> i64 {
7
}
#[Singleton(name = "8", binds = [transform])]
fn Eight() -> i64 {
8
}
// auto_register
#[Singleton(name = "9")]
fn Nine() -> i128 {
9
}
#[Singleton(name = "10", auto_register = false)]
fn Ten() -> i128 {
10
}
// async
#[Transient]
struct A;
#[Transient(async)]
struct B;
// rudi_path
mod a {
pub use rudi::*;
}
#[Transient]
#[di(rudi_path = rudi)]
struct C;
#[Transient]
#[di(rudi_path = a)]
struct D;
// `#[di]` used on `variant` of enum
#[allow(dead_code)]
#[derive(PartialEq, Eq, Debug)]
#[Transient]
enum EF {
#[di]
E,
F,
}
#[tokio::main]
async fn main() {
let mut cx = Context::auto_register();
assert_eq!(cx.resolve::<i8>(), 1);
assert_eq!(cx.resolve_with_name::<i8>("2"), 2);
assert!(!cx.contains_single_with_name::<i16>("3"));
assert!(cx.contains_single_with_name::<i16>("4"));
assert!(cx.contains_provider_with_name::<i32>("5"));
assert!(!cx.contains_provider_with_name::<i32>("6"));
assert!(cx.resolve_option_with_name::<Rc<dyn Debug>>("7").is_none());
assert!(cx.resolve_option_with_name::<Rc<dyn Debug>>("8").is_some());
assert!(cx.get_provider_with_name::<i128>("9").is_some());
assert!(cx.get_provider_with_name::<i128>("10").is_none());
assert!(cx.resolve_option::<A>().is_some());
assert!(cx.resolve_option_async::<B>().await.is_some());
assert!(cx.resolve_option::<C>().is_some());
assert!(cx.resolve_option::<D>().is_some());
assert_eq!(cx.resolve::<EF>(), EF::E);
}§Field and argument attributes example
Although the following example only shows how to use attributes on field, it is the same as using them on argument.
use rudi::{Context, Singleton, Transient};
// name
#[Singleton]
fn One() -> i8 {
1
}
#[Singleton(name = "2")]
fn Two() -> i8 {
2
}
#[Transient]
struct A(i8);
#[Transient]
struct B(#[di(name = "2")] i8);
// option
#[Singleton]
fn Three() -> Option<i16> {
Some(3)
}
#[Singleton]
fn Four() -> i16 {
4
}
#[Transient]
struct C(Option<i16>);
#[Transient]
struct D(#[di(option)] Option<i16>);
// default
#[Transient]
struct E(#[di(default)] i32);
#[Transient]
struct F(#[di(default = 42)] i32);
// vec
#[Singleton]
fn Five() -> Vec<i64> {
vec![5]
}
#[Singleton(eager_create)]
fn Six() -> i64 {
6
}
#[Transient]
struct G(Vec<i64>);
#[Transient]
struct H(#[di(vec)] Vec<i64>);
#[Singleton]
fn Run(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) {
assert_eq!(a.0, 1);
assert_eq!(b.0, 2);
assert_eq!(c.0.unwrap(), 3);
assert_eq!(d.0.unwrap(), 4);
assert_eq!(e.0, 0);
assert_eq!(f.0, 42);
assert_eq!(g.0[0], 5);
assert_eq!(h.0[0], 6);
}
#[Singleton(name = "ref")]
fn Run2(
#[di(ref)] one: &i8,
#[di(ref, name = "2")] two: &i8,
#[di(ref)] three: &Option<i16>,
#[di(ref, option)] four: Option<&i16>,
#[di(ref, default = &0)] zero: &i32,
#[di(ref, default = &42)] forty_two: &i32,
#[di(ref)] five: &Vec<i64>,
#[di(ref, vec)] six: Vec<&i64>,
) {
assert_eq!(one, &1);
assert_eq!(two, &2);
assert_eq!(three, &Some(3));
assert_eq!(four, Some(&4));
assert_eq!(zero, &0);
assert_eq!(forty_two, &42);
assert_eq!(five, &vec![5]);
assert_eq!(six, vec![&6]);
}
mod alias {
use rudi::Singleton;
type OneAndTwo<'a> = &'a i8;
type Three<'a> = &'a Option<i16>;
type Four<'a> = Option<&'a i16>;
type ZeroAndFortyTwo<'a> = &'a i32;
type Five<'a> = &'a Vec<i64>;
type Six<'a> = Vec<&'a i64>;
#[Singleton(name = "ref alias")]
fn Run3(
#[di(ref = i8)] one: OneAndTwo<'_>,
#[di(ref = i8, name = "2")] two: OneAndTwo<'_>,
#[di(ref = Option<i16>)] three: Three<'_>,
#[di(ref = i16, option)] four: Four<'_>,
#[di(ref = i32, default = &0)] zero: ZeroAndFortyTwo<'_>,
#[di(ref = i32, default = &42)] forty_two: ZeroAndFortyTwo<'_>,
#[di(ref = Vec<i64>)] five: Five<'_>,
#[di(ref = i64, vec)] six: Six<'_>,
) {
assert_eq!(one, &1);
assert_eq!(two, &2);
assert_eq!(three, &Some(3));
assert_eq!(four, Some(&4));
assert_eq!(zero, &0);
assert_eq!(forty_two, &42);
assert_eq!(five, &vec![5]);
assert_eq!(six, vec![&6]);
}
}
fn main() {
let mut cx = Context::auto_register();
cx.resolve::<()>();
cx.resolve_with_name::<()>("ref");
cx.resolve_with_name::<()>("ref alias");
}