safe and easy casting from one type to another in Go

cast

GoDoc Build Status Go Report Card

Easy and safe casting from one type to another in Go

Don’t Panic! ... Cast

What is Cast?

Cast is a library to convert between different go types in a consistent and easy way.

Cast provides simple functions to easily convert a number to a string, an interface into a bool, etc. Cast does this intelligently when an obvious conversion is possible. It doesn’t make any attempts to guess what you meant, for example you can only convert a string to an int when it is a string representation of an int such as “8”. Cast was developed for use in Hugo, a website engine which uses YAML, TOML or JSON for meta data.

Why use Cast?

When working with dynamic data in Go you often need to cast or convert the data from one type into another. Cast goes beyond just using type assertion (though it uses that when possible) to provide a very straightforward and convenient library.

If you are working with interfaces to handle things like dynamic content you’ll need an easy way to convert an interface into a given type. This is the library for you.

If you are taking in data from YAML, TOML or JSON or other formats which lack full types, then Cast is the library for you.

Usage

Cast provides a handful of To_____ methods. These methods will always return the desired type. If input is provided that will not convert to that type, the 0 or nil value for that type will be returned.

Cast also provides identical methods To_____E. These return the same result as the To_____ methods, plus an additional error which tells you if it successfully converted. Using these methods you can tell the difference between when the input matched the zero value or when the conversion failed and the zero value was returned.

The following examples are merely a sample of what is available. Please review the code for a complete set.

Example ‘ToString’:

cast.ToString("mayonegg")         // "mayonegg"
cast.ToString(8)                  // "8"
cast.ToString(8.31)               // "8.31"
cast.ToString([]byte("one time")) // "one time"
cast.ToString(nil)                // ""

var foo interface{} = "one more time"
cast.ToString(foo)                // "one more time"

Example ‘ToInt’:

cast.ToInt(8)                  // 8
cast.ToInt(8.31)               // 8
cast.ToInt("8")                // 8
cast.ToInt(true)               // 1
cast.ToInt(false)              // 0

var eight interface{} = 8
cast.ToInt(eight)              // 8
cast.ToInt(nil)                // 0
Owner
Steve Francia
@golang product lead at @google • Author, Speaker, Developer • Creator of @gohugoio, Cobra, Viper & spf13-vim • former @docker & @mongodb
Steve Francia
Comments
  • Add support for maps defined as a json string

    Add support for maps defined as a json string

    • As a fix for https://github.com/spf13/viper/issues/451 , this PR adds support for casting map types from JSON strings. This allows viper to support binding environment variables for more types. This uses the stdlib's encoding/json package for unmarshalling the data and bubbles up unmarshalling errors to the caller.
    • Add test cases to all map casting methods to test this feature
    • Add test cases to TestToDurationSliceE in order to reach 100% total coverage for the lib
  • use errUnableToCast instead of fmt.Errorf

    use errUnableToCast instead of fmt.Errorf

    • [x] added benchmarks
    • [x] use predefined errUnableToCast instead of fmt.Errorf
    • [x] fixed golint

    before:

    go test -bench=.
    goos: darwin
    goarch: amd64
    pkg: github.com/yanmhlv/cast
    BenchmarkTooBool/valid_bool(true)-4         	200000000	         8.25 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/valid_bool(1)-4            	200000000	         8.36 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/invalid_bool("xxx")-4      	30000000	        45.1 ns/op	      48 B/op	       1 allocs/op
    BenchmarkTooBool/invalid_bool(1.0)-4        	 5000000	       302 ns/op	      96 B/op	       3 allocs/op
    PASS
    

    after:

    goos: darwin
    goarch: amd64
    pkg: github.com/yanmhlv/cast
    BenchmarkTooBool/valid_bool(true)-4         	200000000	         8.03 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/valid_bool(1)-4            	200000000	         8.20 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/invalid_bool("xxx")-4      	30000000	        44.8 ns/op	      48 B/op	       1 allocs/op
    BenchmarkTooBool/invalid_bool(1.0)-4        	20000000	        76.0 ns/op	     112 B/op	       2 allocs/op
    PASS
    
  • add time.Time">

    add "yyyy-MM-dd HH:mm:ss" string format > time.Time

    I notice that the Hugo use this project to convert the string to time.Time. When I migrate to Hugo from Hexo (another static blog generator). The Hugo said :

    page.go:750: Failed to parse date '2016-03-06 15:28:01' in page xxxxx.md

    I hope you can merge this Pull request, so that those people who migrate to Hugo from Hexo are easy to use Hugo to generate the static blog.

    Thanks. ^_^

  • Add ToTimeInDefaultLocation/E

    Add ToTimeInDefaultLocation/E

    Go's time parsing uses UTC when the format doesn't have a timezone, and has even weirder behavior when it has a zone name but no numeric offset. A caller to cast.ToTime cannot know if the returned time was explicitly in UTC or was set by default, so they cannot fix it. These new functions allow a user to supply a different timezone to default to, with nil using the local zone.

    This addresses spf13/hugo#1882 by allowing that code to use cast.ToTimeInDefaultLocation(raw, nil).

    A note on the implementation: It's not possible to use time.ParseInLocation because parsing with a format that has a named zone but no numeric offset (such as time.RC1123Z) returns a time with a 0 offset in that name iff that's not the current local timezone (!!). You can verify this by changing like 521 to use time.ParseInLocation(format.format, s, defaultLocation) and commenting out the manual override in the body of the if clause – tests will fail.

    I think the root cause is that the time pkg has two parsing functions: time.Parse and time.ParseInLocation, both of which use a private workhorse fn time.parse. That private fn takes two zones: a default and what's considered "local". I'm not sure, but I think in order to get the effect we want, we'd need to call that fn with our default and the current local zone. However, time.Parse uses default of UTC, whereas time.ParseInLocation uses the given zone for both default & local.

  • Adding ToStringMapStringSlice Method

    Adding ToStringMapStringSlice Method

    This should fix #15 and from my personal testing works. I did notice there is no test method for a ToStringMapString so I didn't add a test for ToStringMapStringSlice. Having a ToStringMapString test would have been nice to follow/modify for a TestToStringMapStringSlice function.

  • Fix multiple interface{} values to ToStringMapStringSliceE

    Fix multiple interface{} values to ToStringMapStringSliceE

    This fixes passing map[string]interface{} with the map containing multiple strings. Previously the result is a slice with an empty string in it.

    I discovered when using https://github.com/spf13/viper with the following:

    {
      "key": [
        "val1",
        "val2"
      ]
    }
    

    Viper resolved this to map[string]interface{} and the val key became []string{""}

  • Add bool case to ToStringE

    Add bool case to ToStringE

    Casting an interface{} of type bool to string was missing. So added with this PR.

    s,err := ToStringE(true)
    fmt.Printf("%q Err: %s",s,err)
    // Output: "true" Err:
    
  • 'cast.ToString()' conversion error

    'cast.ToString()' conversion error

    A strange error occurs when I use toString. The example code is as follows:

    package main
    
    import (
    	"fmt"
    	"github.com/spf13/cast"
    )
    type NewString string
    
    func main() {
    	var test NewString
    	test = "this is a test string"
    	fmt.Println("original: ",test)
    	fmt.Println("use string() func: ",string(test))
    	fmt.Println("use cast.ToString() func: ",cast.ToString(test))
    	fmt.Println(cast.ToString(test)=="")
    }
    

    Output:

    original:  this is a test string
    use string() func:  this is a test string
    use cast.ToString() func:  
    true
    
  • StringToDate: +more RFC3339 forms without TZ colon

    StringToDate: +more RFC3339 forms without TZ colon

    Adds a form to handle the common format strftime("%FT%T%z"), which omits the (optional) colon from the timezone. Also adds a matching T-omitted form.

  • Can 「cast.toString()」API Support User-defined String Type?

    Can 「cast.toString()」API Support User-defined String Type?

    https://github.com/spf13/cast/blob/2b0eb0f724e320b655240e331aef36d1175986c2/caste.go#L903 when i define a type as: type InstStatus string this cast.toString() API cannot work cause 「indirectToStringerOrErrorand」will not get the type's Elem with reflect package API, it will turn to default branch and the result is ""

  • add support to cast int64 slices

    add support to cast int64 slices

    What does this MR does?

    Create ToInt64SliceE and ToInt64Slice method to support int64 slice casting;

    How to test?

    • Run the unit tests:
    go test -v  -run TestToInt64SliceE .
    
    • Test code:
    input := []interface{}{1.2, 3.2}
    v = ToInt64Slice(input)
    fmt.Println(v)
    
  • doesn't handle named types

    doesn't handle named types

    I'd expect this test to pass:

    func TestToFloat64NamedType(t *testing.T) {
    	type X float64
    	f := ToFloat64(X(2))
    	if f != 2.0 {
    		t.Fatalf("want %#v, got %#v", 2.0, f)
    	}
    }
    

    The fix is pretty easy. Near the beginning of ToFloat64E, just after the call to indirect, add something like:

    	v := reflect.ValueOf(i)
    	if v.CanFloat() {
    		return v.Float(), nil
    	}
    

    I'm filing an issue rather than sending a PR because the proper fix applies this to all conversions, not just floats, and that's a pretty substantive change, so I wanted to discuss first.

  • cast protobuf enum return 0

    cast protobuf enum return 0

    version: 1.5 1.3

    test.proto
    
    enum N {
       DEFAULT = 0;
       ONE =  1;
    }
    
    message Test {
        N  num  = 1;
    }
    
    
    main.go
    
    a := &pb.Test{ Num:  123 }
    
    cast.toInt32(a)   //return 0
    int32(a)   //return 123
    
    
  • cast.ToInt64E(

    cast.ToInt64E("16025123132364928") return 0

    parser string to int64 should use strconv.ParseInt(trimZeroDecimal(s), 10, 64) not the follow; v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return v, nil }

  • cast v1.5.0 bug

    cast v1.5.0 bug

    func ToUint64E(i interface{}) (uint64, error) use the v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) error:strconv.ParseInt: parsing "11166014772858028509": value out of range unable to cast "11166014772858028509" of type string to uint64

    case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint64(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)

  • Support cast defined type

    Support cast defined type

    // You can edit this code!
    // Click here and start typing.
    package main
    
    import (
    	"fmt"
    
    	"github.com/spf13/cast"
    )
    
    type Hoge = string
    type Fuga string
    
    func main() {
    	h := Hoge("hoge")
    	f := Fuga("fuga")
    	fmt.Println(h, f)
    	fmt.Println(cast.ToString(h), cast.ToString(f))
    }
    
    // output
    hoge fuga
    hoge 
    
Related tags
🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

?? github.com/elliotchance/pie Enjoy a slice! pie is a library of utility functions for common operations on slices and maps. Quick Start FAQ What are

Dec 30, 2022
Yet another semantic version incrementor and tagger for git

git-tag-inc Increments the version number and tags it. (You will need to push) Usage ./git-tag-inc [major] [minor] [release] [test] [uat] git-tag-in

Apr 30, 2022
Yet another StructTag, with some features liked cache and alias.

Yet another StructTag, with some features liked cache and alias.

Nov 1, 2021
a thread-safe concurrent map for go

concurrent map As explained here and here, the map type in Go doesn't support concurrent reads and writes. concurrent-map provides a high-performance

Jan 8, 2023
Lightweight, Simple, Quick, Thread-Safe Golang Stack Implementation

stack Lightweight, Simple, Quick, Thread-Safe Golang Stack Implementation Purpose Provide a fast, thread safe, and generic Golang Stack API with minim

May 3, 2022
A simple thread-safe, fixed size LRU written in Go. Based on dominictarr's Hashlru Algorithm. 🔃

go-hashlru A simple thread-safe, fixed size LRU written in Go. Based on dominictarr's Hashlru Algorithm. ?? Uses map[interface{}]interface{} to allow

Dec 5, 2022
A thread-safe concurrent map for go

concurrent map Original repo didn't support go mod and no any tags,so I forkd this repo add go mod support and patch a tag on this repo. No any code c

Dec 7, 2021
Utility to restrict which package is allowed to import another package.

go-import-rules Utility to restrict which package is allowed to import another package. This tool will read import-rules.yaml or import-rules.yml in t

Jan 7, 2022
efaceconv - Code generation tool for high performance conversion from interface{} to immutable type without allocations.

efaceconv High performance conversion from interface{} to immutable types without additional allocations This is tool for go generate and common lib (

May 14, 2022
Type-driven code generation for Go

What’s this? gen is a code-generation tool for Go. It’s intended to offer generics-like functionality on your types. Out of the box, it offers offers

Jan 4, 2023
Go library for HTTP content type negotiation

Content-Type support library for Go This library can be used to parse the value Content-Type header (if one is present) and select an acceptable media

Jul 10, 2022
Quickly query a Terraform provider's data type.

Terraform Query Quickly query a Terraform provider's data type. Such as a GitHub repository: ➜ ~ tfq github_repository full_name hashicorp/terraform |

Oct 12, 2021
Optional type using Go 1.18 generics.

go.eth-p.dev/goptional Generic Optional (or Go Optional, if you prefer) goptional is a package that provides an implementation of an Optional[T] monad

Apr 2, 2022
A protoc plugin that generates fieldmask paths as static type properties for proto messages

protoc-gen-fieldmask A protoc plugin that generates fieldmask paths as static ty

Nov 3, 2022
Di - A (very) WIP Go 1.18+ generic dependency injection package based on type reflection

di A (very) WIP Go 1.18+ generic dependency injection package based on type refl

Apr 26, 2022
The one-stop shop for most common Go functions
The one-stop shop for most common Go functions

Pandati The one stop shop for most common Go functions Table of contents Pandati The one stop shop for most common Go functions Table of contents Purp

Mar 21, 2022
Go implementation Welford’s method for one-pass variance computation

Welford - Online method of calculating variance and standard deviation Go implementation Welford’s method for one-pass variance computation with D. H.

Jan 6, 2023
A fully Go userland with Linux bootloaders! u-root can create a one-binary root file system (initramfs) containing a busybox-like set of tools written in Go.

u-root Description u-root embodies four different projects. Go versions of many standard Linux tools, such as ls, cp, or shutdown. See cmds/core for m

Dec 29, 2022
📚 Clone all your repositories from GitHub with one command!

An utility to clone easily all your repositories from GitHub. The process is done concurrently so it is quite fast.

Apr 6, 2022