Git Product home page Git Product logo

strong-type's Introduction

strong-type

strong-type is a Rust crate that offers macros to easily create strongly typed and named primitive and string types. Strong typing helps in making code more expressive and less prone to errors, ensuring that each type is used in its intended way.

use strong_type::StrongType;

#[derive(StrongType)]
struct Timestamp(i64);

let timestamp = Timestamp::new(1701620628123456789);
println!("{}", timestamp); // Timestamp(1701620628123456789)

Features

  • Derive trait StrongType: Create a named strong type.

    • The macro automatically implement common traits like Clone, Debug, Default, PartialEq, PartialOrd, Send, and Sync. It also implements Display by default, unless overridden by the custom_display attribute.
    • Conditionally, based on the underlying data type, traits like Copy, Eq, Ord, Hash may also be implemented. For primitive data types like i32 or bool, these additional traits will be automatically included.
    • Numeric types, both integer and floating-point, also implement constants MIN, MAX, INFINITY, NEG_INFINITY, and ZERO. Additionally, for floating-point types, NAN is implemented.
  • Attributes:

    • Adding the following attributes to #[strong_type(...)] allows for additional features:
      • auto_operators: Automatically implements relevant arithmetic (for numeric types) or logical (for boolean types) operators.
      • custom_display: Allows users to manually implement the Display trait, providing an alternative to the default display format.
      • conversion: Automatically implements From and Into traits for the underlying type. This is optional since conversion may make strong types less distinct.
      • underlying: Specifies the underlying primitive type for nested strong types.

Installation

Add strong-type to your Cargo.toml:

[dependencies]
strong-type = "0.11"

Supported underlying types:

  • Integer types: i8, i16, i32, i64, i128, isize
  • Unsigned integer types: u8, u16, u32, u64, u128, usize
  • Floating-point types: f32, f64
  • Boolean type: bool
  • char
  • String
  • Strong types of the above types

Examples

Creating a named strong type:

With a private field:

use strong_type::StrongType;

#[derive(StrongType)]
struct Tag(String);

let tag = Tag::new("dev");
const TAG: Tag = Tag::const_new("prod");

With a public field:

use strong_type::StrongType;

#[derive(StrongType)]
struct Timestamp(pub i64);

let timestamp = Timestamp(1701620628123456789);
println!("{}", timestamp); // Timestamp(1701620628123456789)

Demonstrating type distinctiveness:

use strong_type::StrongType;
use std::any::Any;

#[derive(StrongType)]
struct Second(i32);

#[derive(StrongType)]
struct Minute(i32);

let x = Second::new(2);
let y = Second::new(3);
let z = Minute::new(3);

assert_eq!(x.type_id(), y.type_id()); // Same type: Second
assert_ne!(y.type_id(), z.type_id()); // Different types: Second versus Minute

Utilizing Hashability:

use std::collections::HashSet;

#[derive(StrongType)]
struct Tag(String);

let mut map = HashSet::<Tag>::new();
map.insert(Tag::new("dev"));
map.insert(Tag::new("prod"));
assert_eq!(map.len(), 2);

Named integer type with arithmetic operations:

use strong_type::StrongType;

#[derive(StrongType)]
#[strong_type(auto_operators)]
struct Nanosecond(u32);

let x = Nanosecond::new(2);
let y = Nanosecond::new(3);
let z = Nanosecond::default();

assert_eq!(x.value(), 2);
assert_eq!(y.value(), 3);
assert_eq!(z.value(), 0);
assert!(x < y);
assert!(y >= x);
assert_eq!(x + y, Nanosecond(5));

Named bool type with logical operations:

use strong_type::StrongType;

#[derive(StrongType)]
#[strong_type(auto_operators)]

struct IsTrue(bool);

let x = IsTrue::new(true);
let y = IsTrue::new(false);

assert_eq!(x & y, IsTrue::new(false));
assert_eq!(x | y, IsTrue::new(true));
assert_eq!(x ^ y, IsTrue::new(true));
assert_eq!(!x, IsTrue::new(false));

Custom display implementation with custom_display:

use std::fmt::{Display, Formatter, Result};
use strong_type::StrongType;

#[derive(StrongType)]
#[strong_type(custom_display)]

struct Second(f64);

impl Display for Second {
   fn fmt(&self, f: &mut Formatter) -> Result {
      write!(f, "Second({:.2})", &self.0)
   }
}

println!("{}", Second::new(std::f64::consts::E)); // "Second(2.72)"
println!("{:?}", Second::new(std::f64::consts::E)); // "Second { value: 2.718281828459045 }"

Nested strong types:

#[derive(StrongType)]
#[strong_type(auto_operators)]
struct Dollar(i32);

#[derive(StrongType)]
#[strong_type(auto_operators, underlying = i32)]
struct Cash(Dollar);

#[derive(StrongType)]
#[strong_type(underlying = i32)]
struct Coin(Cash);

Caveats:

  • When using #[derive(StrongType)], the traits Eq and PartialEq are implemented with impl. As a result, StructuralEq and StructuralPartialEq remain unimplemented, preventing pattern matching with strong-typed primitives.

strong-type's People

Contributors

yunjhongwu avatar

Stargazers

Jan Vincent Liwanag avatar

Watchers

 avatar  avatar

strong-type's Issues

StrongType constructor not accessible from external module

Hi Yun-Jhong Wu,

Thanks for the crate StrongType, This is a great one.
Sorry if the issue I have is not very well explained, I am a newbie to rust.

I have external file, say timestamp.rs, where I define a strong type:
#[derive(StrongType)]
#[strong_type(auto_operators)]
pub struct Timestamp(usize);

When using in the main.rs an expression such as:

Timestamp(123456789) + Timestamp(987654321)

I get an error in the main.rs:

  • cannot initialize a tuple struct which contains private fields [...]
  • timestamp.rs(8, 20): constructor is not visible here due to private fields
  • timestamp.rs(8, 20): consider making the field publicly accessible: pub

If in timestamp.rs I do add pub in the declaration,

#[derive(StrongType)]
#[strong_type(auto_operators)]
pub struct NbTeams(pub usize);

I get another error, in the timestamp.rs:

  • proc-macro derive panicked
  • message: Strong type must be a tuple struct with one private field.

There might be some proper way of doing it, but I am not yet good enough at rust to get it.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.