diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs
index 583f46ea93..7bf70366d7 100644
--- a/bindgen-integration/build.rs
+++ b/bindgen-integration/build.rs
@@ -192,6 +192,8 @@ fn setup_macro_test() {
         .enable_cxx_namespaces()
         .default_enum_style(EnumVariation::Rust {
             non_exhaustive: false,
+            safe_conversion: false,
+            unsafe_conversion: false,
         })
         .raw_line("pub use self::root::*;")
         .raw_line("extern { fn my_prefixed_function_to_remove(i: i32); }")
diff --git a/bindgen-tests/tests/expectations/tests/issue-2646.rs b/bindgen-tests/tests/expectations/tests/issue-2646.rs
new file mode 100644
index 0000000000..46a5c463a4
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/issue-2646.rs
@@ -0,0 +1,100 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum Plain {
+    Plain1 = 0,
+    Plain2 = 1,
+    Plain3 = 2,
+}
+pub type TryFromRaw_ctype = ::std::os::raw::c_int;
+impl TryFromRaw {
+    pub const TFR1: TryFromRaw_ctype = -1;
+    pub const TFR2: TryFromRaw_ctype = 5;
+    pub const TFR3: TryFromRaw_ctype = 6;
+}
+#[repr(i32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum TryFromRaw {
+    TFR1 = -1,
+    TFR2 = 5,
+    TFR3 = 6,
+}
+pub struct TryFromRawError(TryFromRaw_ctype);
+impl TryFromRawError {
+    #[must_use]
+    pub fn value(&self) -> TryFromRaw_ctype {
+        self.0
+    }
+}
+impl std::convert::TryFrom<TryFromRaw_ctype> for TryFromRaw {
+    type Error = TryFromRawError;
+    fn try_from(v: TryFromRaw_ctype) -> Result<Self, Self::Error> {
+        match v {
+            -1 => Ok(TryFromRaw::TFR1),
+            5 => Ok(TryFromRaw::TFR2),
+            6 => Ok(TryFromRaw::TFR3),
+            _ => Err(TryFromRawError(v)),
+        }
+    }
+}
+pub type FromRawUnchecked_ctype = ::std::os::raw::c_uint;
+impl FromRawUnchecked {
+    pub const FRU1: FromRawUnchecked_ctype = 6;
+    pub const FRU2: FromRawUnchecked_ctype = 10;
+    pub const FRU3: FromRawUnchecked_ctype = 11;
+}
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum FromRawUnchecked {
+    FRU1 = 6,
+    FRU2 = 10,
+    FRU3 = 11,
+}
+impl FromRawUnchecked {
+    const unsafe fn from_ctype_unchecked(v: FromRawUnchecked_ctype) -> Self {
+        std::mem::transmute(v)
+    }
+}
+impl Both {
+    pub const Both3: Both = Both::Both1;
+}
+pub type Both_ctype = ::std::os::raw::c_int;
+impl Both {
+    pub const Both1: Both_ctype = 0;
+    pub const Both2: Both_ctype = -1;
+}
+#[repr(i32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum Both {
+    Both1 = 0,
+    Both2 = -1,
+}
+pub struct BothError(Both_ctype);
+impl BothError {
+    #[must_use]
+    pub fn value(&self) -> Both_ctype {
+        self.0
+    }
+}
+impl std::convert::TryFrom<Both_ctype> for Both {
+    type Error = BothError;
+    fn try_from(v: Both_ctype) -> Result<Self, Self::Error> {
+        match v {
+            0 => Ok(Both::Both1),
+            -1 => Ok(Both::Both2),
+            _ => Err(BothError(v)),
+        }
+    }
+}
+impl Both {
+    const unsafe fn from_ctype_unchecked(v: Both_ctype) -> Self {
+        std::mem::transmute(v)
+    }
+}
+#[repr(u32)]
+#[non_exhaustive]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum NonExhaustive {
+    Ex1 = 0,
+    Ex2 = 1,
+}
diff --git a/bindgen-tests/tests/headers/issue-2646.h b/bindgen-tests/tests/headers/issue-2646.h
new file mode 100644
index 0000000000..63068780eb
--- /dev/null
+++ b/bindgen-tests/tests/headers/issue-2646.h
@@ -0,0 +1,30 @@
+// bindgen-flags: --rustified-enum 'Plain.*' --rustified-enum 'TryFromRaw.*=try_from_raw' --rustified-enum='FromRawUnchecked.*=from_raw_unchecked' --rustified-enum='Both.*=try_from_raw,from_raw_unchecked' --rustified-enum 'NonExhaustive.*=non_exhaustive'
+
+enum Plain {
+    Plain1,
+    Plain2,
+    Plain3
+};
+
+enum TryFromRaw {
+    TFR1 = -1,
+    TFR2 = 5,
+    TFR3
+};
+
+enum FromRawUnchecked {
+    FRU1 = 6,
+    FRU2 = 10,
+    FRU3 = 11,
+};
+
+enum Both {
+    Both1,
+    Both2 = -1,
+    Both3,
+};
+
+enum NonExhaustive {
+    Ex1,
+    Ex2,
+};
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index f5518e432d..06fa794777 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -3172,6 +3172,12 @@ pub enum EnumVariation {
     Rust {
         /// Indicates whether the generated struct should be `#[non_exhaustive]`
         non_exhaustive: bool,
+        /// Indicates whether the generated struct should have a safe conversion from an integer
+        /// value.
+        safe_conversion: bool,
+        /// Indicates whether the generated struct should have an unsafe conversion from an integer
+        /// value.
+        unsafe_conversion: bool,
     },
     /// The code for this enum will use a newtype
     NewType {
@@ -3203,11 +3209,20 @@ impl fmt::Display for EnumVariation {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let s = match self {
             Self::Rust {
-                non_exhaustive: false,
-            } => "rust",
-            Self::Rust {
-                non_exhaustive: true,
-            } => "rust_non_exhaustive",
+                non_exhaustive,
+                safe_conversion,
+                unsafe_conversion,
+                ..
+            } => match (non_exhaustive, safe_conversion, unsafe_conversion) {
+                (false, false, false) => "rust",
+                (false, true, true) => "rust_conversions",
+                (false, true, _) => "rust_safe_conversion",
+                (false, _, true) => "rust_unsafe_conversion",
+                (true, false, false) => "rust_non_exhaustive",
+                (true, true, true) => "rust_non_exhaustive_conversions",
+                (true, true, _) => "rust_non_exhaustive_safe_conversion",
+                (true, _, true) => "rust_non_exhaustive_unsafe_conversion",
+            },
             Self::NewType {
                 is_bitfield: true, ..
             } => "bitfield",
@@ -3236,10 +3251,46 @@ impl FromStr for EnumVariation {
         match s {
             "rust" => Ok(EnumVariation::Rust {
                 non_exhaustive: false,
+                safe_conversion: false,
+                unsafe_conversion: false,
+            }),
+            "rust_conversions" => Ok(EnumVariation::Rust {
+                non_exhaustive: false,
+                safe_conversion: true,
+                unsafe_conversion: true,
+            }),
+            "rust_safe_conversion" => Ok(EnumVariation::Rust {
+                non_exhaustive: false,
+                safe_conversion: true,
+                unsafe_conversion: false,
+            }),
+            "rust_unsafe_conversion" => Ok(EnumVariation::Rust {
+                non_exhaustive: false,
+                safe_conversion: false,
+                unsafe_conversion: true,
             }),
             "rust_non_exhaustive" => Ok(EnumVariation::Rust {
                 non_exhaustive: true,
+                safe_conversion: false,
+                unsafe_conversion: false,
+            }),
+            "rust_non_exhaustive_conversions" => Ok(EnumVariation::Rust {
+                non_exhaustive: true,
+                safe_conversion: true,
+                unsafe_conversion: true,
+            }),
+            "rust_non_exhaustive_safe_conversion" => Ok(EnumVariation::Rust {
+                non_exhaustive: true,
+                safe_conversion: true,
+                unsafe_conversion: false,
             }),
+            "rust_non_exhaustive_unsafe_conversion" => {
+                Ok(EnumVariation::Rust {
+                    non_exhaustive: true,
+                    safe_conversion: false,
+                    unsafe_conversion: true,
+                })
+            }
             "bitfield" => Ok(EnumVariation::NewType {
                 is_bitfield: true,
                 is_global: false,
@@ -3271,8 +3322,14 @@ enum EnumBuilder<'a> {
     Rust {
         attrs: Vec<proc_macro2::TokenStream>,
         ident: Ident,
-        tokens: proc_macro2::TokenStream,
+        typedef: Option<Ident>,
+        typedef_tokens: proc_macro2::TokenStream,
+        const_tokens: Vec<proc_macro2::TokenStream>,
+        rustified_tokens: Vec<proc_macro2::TokenStream>,
+        safe_conversion_tokens: Vec<proc_macro2::TokenStream>,
         emitted_any_variants: bool,
+        safe_conversion: bool,
+        unsafe_conversion: bool,
     },
     NewType {
         canonical_name: &'a str,
@@ -3300,7 +3357,7 @@ impl<'a> EnumBuilder<'a> {
     fn new(
         name: &'a str,
         mut attrs: Vec<proc_macro2::TokenStream>,
-        repr: &syn::Type,
+        repr: &EnumRepr,
         enum_variation: EnumVariation,
         has_typedef: bool,
     ) -> Self {
@@ -3310,29 +3367,73 @@ impl<'a> EnumBuilder<'a> {
             EnumVariation::NewType {
                 is_bitfield,
                 is_global,
-            } => EnumBuilder::NewType {
-                canonical_name: name,
-                tokens: quote! {
-                    #( #attrs )*
-                    pub struct #ident (pub #repr);
-                },
-                is_bitfield,
-                is_global,
-            },
+            } => {
+                let repr = match repr {
+                    EnumRepr::Other(r) => r,
+                    EnumRepr::Rust(..) => panic!(
+                        "Should never get this variant for new type enum"
+                    ),
+                };
+                EnumBuilder::NewType {
+                    canonical_name: name,
+                    tokens: quote! {
+                        #( #attrs )*
+                        pub struct #ident (pub #repr);
+                    },
+                    is_bitfield,
+                    is_global,
+                }
+            }
 
-            EnumVariation::Rust { .. } => {
+            EnumVariation::Rust {
+                safe_conversion,
+                unsafe_conversion,
+                ..
+            } => {
+                let (untranslated_repr, translated_repr) = match repr {
+                    EnumRepr::Rust(un_r, t_r) => (un_r, t_r),
+                    EnumRepr::Other(..) => panic!(
+                        "Should never get this variant for rustified enum"
+                    ),
+                };
+                let ctype = if safe_conversion || unsafe_conversion {
+                    Some(Ident::new(
+                        format!("{ident}_ctype").as_str(),
+                        Span::call_site(),
+                    ))
+                } else {
+                    None
+                };
                 // `repr` is guaranteed to be Rustified in Enum::codegen
-                attrs.insert(0, quote! { #[repr( #repr )] });
-                let tokens = quote!();
+                attrs.insert(0, quote! { #[repr( #translated_repr)] });
                 EnumBuilder::Rust {
                     attrs,
+                    const_tokens: vec![],
+                    typedef_tokens: if ctype.is_some() {
+                        quote! {
+                            pub type #ctype = #untranslated_repr;
+                        }
+                    } else {
+                        quote!()
+                    },
                     ident,
-                    tokens,
+                    typedef: ctype,
+                    rustified_tokens: vec![],
+                    safe_conversion_tokens: vec![],
                     emitted_any_variants: false,
+                    safe_conversion,
+                    unsafe_conversion,
                 }
             }
 
             EnumVariation::Consts => {
+                let repr = match repr {
+                    EnumRepr::Other(r) => r,
+                    EnumRepr::Rust(..) => {
+                        panic!("Should never get this variant for consts enum")
+                    }
+                };
+
                 let mut variants = Vec::new();
 
                 if !has_typedef {
@@ -3346,6 +3447,13 @@ impl<'a> EnumBuilder<'a> {
             }
 
             EnumVariation::ModuleConsts => {
+                let repr = match repr {
+                    EnumRepr::Other(r) => r,
+                    EnumRepr::Rust(..) => {
+                        panic!("Should never get this variant for module consts enum")
+                    }
+                };
+
                 let ident = Ident::new(
                     CONSTIFIED_ENUM_MODULE_REPR_NAME,
                     Span::call_site(),
@@ -3365,7 +3473,7 @@ impl<'a> EnumBuilder<'a> {
 
     /// Add a variant to this enum.
     fn with_variant(
-        self,
+        mut self,
         ctx: &BindgenContext,
         variant: &EnumVariant,
         mangling_prefix: Option<&str>,
@@ -3375,13 +3483,17 @@ impl<'a> EnumBuilder<'a> {
     ) -> Self {
         let variant_name = ctx.rust_mangle(variant.name());
         let is_rust_enum = self.is_rust_enum();
-        let expr = match variant.val() {
+        let (is_bool, expr) = match variant.val() {
             EnumVariantValue::Boolean(v) if is_rust_enum => {
-                helpers::ast_ty::uint_expr(u64::from(v))
+                (true, helpers::ast_ty::uint_expr(u64::from(v)))
+            }
+            EnumVariantValue::Boolean(v) => (true, quote!(#v)),
+            EnumVariantValue::Signed(v) => {
+                (false, helpers::ast_ty::int_expr(v))
+            }
+            EnumVariantValue::Unsigned(v) => {
+                (false, helpers::ast_ty::uint_expr(v))
             }
-            EnumVariantValue::Boolean(v) => quote!(#v),
-            EnumVariantValue::Signed(v) => helpers::ast_ty::int_expr(v),
-            EnumVariantValue::Unsigned(v) => helpers::ast_ty::uint_expr(v),
         };
 
         let mut doc = quote! {};
@@ -3394,22 +3506,43 @@ impl<'a> EnumBuilder<'a> {
 
         match self {
             EnumBuilder::Rust {
-                attrs,
-                ident,
-                tokens,
-                emitted_any_variants: _,
+                ref ident,
+                ref typedef,
+                ref mut const_tokens,
+                ref mut rustified_tokens,
+                ref mut safe_conversion_tokens,
+                ref mut emitted_any_variants,
+                safe_conversion,
+                unsafe_conversion,
+                ..
             } => {
                 let name = ctx.rust_ident(variant_name);
-                EnumBuilder::Rust {
-                    attrs,
-                    ident,
-                    tokens: quote! {
-                        #tokens
+                if safe_conversion || unsafe_conversion {
+                    let const_name = Ident::new(
+                        name.to_string().as_str(),
+                        Span::call_site(),
+                    );
+                    let add_cmp = if is_bool {
+                        quote! { != 0 }
+                    } else {
+                        quote!()
+                    };
+                    const_tokens.push(quote! {
                         #doc
-                        #name = #expr,
-                    },
-                    emitted_any_variants: true,
+                        pub const #const_name: #typedef = #expr #add_cmp;
+                    });
                 }
+                rustified_tokens.push(quote! {
+                    #doc
+                    #name = #expr ,
+                });
+                safe_conversion_tokens.push(quote! {
+                    #expr => Ok(#ident::#name) ,
+                });
+
+                *emitted_any_variants = true;
+
+                self
             }
 
             EnumBuilder::NewType {
@@ -3488,21 +3621,87 @@ impl<'a> EnumBuilder<'a> {
             EnumBuilder::Rust {
                 attrs,
                 ident,
-                tokens,
+                typedef,
+                typedef_tokens,
+                const_tokens,
+                rustified_tokens,
+                safe_conversion_tokens,
                 emitted_any_variants,
+                safe_conversion,
+                unsafe_conversion,
                 ..
             } => {
                 let variants = if emitted_any_variants {
-                    tokens
+                    rustified_tokens
+                } else {
+                    vec![quote!(__bindgen_cannot_repr_c_on_empty_enum = 0)]
+                };
+
+                let consts = if safe_conversion || unsafe_conversion {
+                    quote! {
+                        impl #ident {
+                            #( #const_tokens )*
+                        }
+                    }
+                } else {
+                    quote!()
+                };
+
+                let safe_conversion = if safe_conversion {
+                    let prefix = ctx.trait_prefix();
+                    let error_type = Ident::new(
+                        format!("{ident}Error").as_str(),
+                        Span::call_site(),
+                    );
+                    quote! {
+                        pub struct #error_type(#typedef);
+
+                        impl #error_type {
+                            #[must_use]
+                            pub fn value(&self) -> #typedef {
+                                self.0
+                            }
+                        }
+
+                        impl #prefix::convert::TryFrom<#typedef> for #ident {
+                            type Error = #error_type;
+
+                            fn try_from(v: #typedef) -> Result<Self, Self::Error> {
+                                match v {
+                                    #( #safe_conversion_tokens )*
+                                    _ => Err(#error_type(v))
+                                }
+                            }
+                        }
+                    }
                 } else {
-                    quote!(__bindgen_cannot_repr_c_on_empty_enum = 0)
+                    quote!()
+                };
+
+                let unsafe_conversion = if unsafe_conversion {
+                    quote! {
+                        impl #ident {
+                            const unsafe fn from_ctype_unchecked(v: #typedef) -> Self {
+                                std::mem::transmute(v)
+                            }
+                        }
+                    }
+                } else {
+                    quote!()
                 };
 
                 quote! {
+                    #typedef_tokens
+                    #consts
+
                     #( #attrs )*
                     pub enum #ident {
-                        #variants
+                        #( #variants )*
                     }
+
+                    #safe_conversion
+
+                    #unsafe_conversion
                 }
             }
             EnumBuilder::NewType {
@@ -3577,6 +3776,72 @@ impl<'a> EnumBuilder<'a> {
     }
 }
 
+fn handle_translation(
+    ctx: &BindgenContext,
+    layout: Option<&Layout>,
+    item: &Item,
+    repr: Option<&Type>,
+    translate: bool,
+) -> syn::Type {
+    match repr {
+        Some(repr)
+            if !ctx.options().translate_enum_integer_types && !translate =>
+        {
+            repr.to_rust_ty_or_opaque(ctx, item)
+        }
+        repr => {
+            // An enum's integer type is translated to a native Rust
+            // integer type in 3 cases:
+            // * the enum is Rustified and we need a translated type for
+            //   the repr attribute
+            // * the representation couldn't be determined from the C source
+            // * it was explicitly requested as a bindgen option
+
+            let kind = if let Some(repr) = repr {
+                match *repr.canonical_type(ctx).kind() {
+                    TypeKind::Int(int_kind) => int_kind,
+                    _ => panic!("Unexpected type as enum repr"),
+                }
+            } else {
+                warn!(
+                    "Guessing type of enum! Forward declarations of enums \
+                             shouldn't be legal!"
+                );
+                IntKind::Int
+            };
+
+            let signed = kind.is_signed();
+            let size = layout
+                .map(|l| l.size)
+                .or_else(|| kind.known_size())
+                .unwrap_or(0);
+
+            let translated = match (signed, size) {
+                (true, 1) => IntKind::I8,
+                (false, 1) => IntKind::U8,
+                (true, 2) => IntKind::I16,
+                (false, 2) => IntKind::U16,
+                (true, 4) => IntKind::I32,
+                (false, 4) => IntKind::U32,
+                (true, 8) => IntKind::I64,
+                (false, 8) => IntKind::U64,
+                _ => {
+                    warn!("invalid enum decl: signed: {signed}, size: {size}");
+                    IntKind::I32
+                }
+            };
+
+            Type::new(None, None, TypeKind::Int(translated), false)
+                .to_rust_ty_or_opaque(ctx, item)
+        }
+    }
+}
+
+enum EnumRepr {
+    Rust(syn::Type, syn::Type),
+    Other(syn::Type),
+}
+
 impl CodeGenerator for Enum {
     type Extra = Item;
     type Return = ();
@@ -3596,69 +3861,37 @@ impl CodeGenerator for Enum {
         let layout = enum_ty.layout(ctx);
         let variation = self.computed_enum_variation(ctx, item);
 
-        let repr_translated;
-        let repr = match self.repr().map(|repr| ctx.resolve_type(repr)) {
-            Some(repr)
-                if !ctx.options().translate_enum_integer_types &&
-                    !variation.is_rust() =>
-            {
-                repr
-            }
-            repr => {
-                // An enum's integer type is translated to a native Rust
-                // integer type in 3 cases:
-                // * the enum is Rustified and we need a translated type for
-                //   the repr attribute
-                // * the representation couldn't be determined from the C source
-                // * it was explicitly requested as a bindgen option
-
-                let kind = if let Some(repr) = repr {
-                    match *repr.canonical_type(ctx).kind() {
-                        TypeKind::Int(int_kind) => int_kind,
-                        _ => panic!("Unexpected type as enum repr"),
-                    }
-                } else {
-                    warn!(
-                        "Guessing type of enum! Forward declarations of enums \
-                         shouldn't be legal!"
-                    );
-                    IntKind::Int
-                };
-
-                let signed = kind.is_signed();
-                let size = layout
-                    .map(|l| l.size)
-                    .or_else(|| kind.known_size())
-                    .unwrap_or(0);
-
-                let translated = match (signed, size) {
-                    (true, 1) => IntKind::I8,
-                    (false, 1) => IntKind::U8,
-                    (true, 2) => IntKind::I16,
-                    (false, 2) => IntKind::U16,
-                    (true, 4) => IntKind::I32,
-                    (false, 4) => IntKind::U32,
-                    (true, 8) => IntKind::I64,
-                    (false, 8) => IntKind::U64,
-                    _ => {
-                        warn!(
-                            "invalid enum decl: signed: {signed}, size: {size}"
-                        );
-                        IntKind::I32
-                    }
-                };
-
-                repr_translated =
-                    Type::new(None, None, TypeKind::Int(translated), false);
-                &repr_translated
-            }
+        let repr = if variation.is_rust() {
+            EnumRepr::Rust(
+                handle_translation(
+                    ctx,
+                    layout.as_ref(),
+                    item,
+                    self.repr().map(|repr| ctx.resolve_type(repr)),
+                    false,
+                ),
+                handle_translation(
+                    ctx,
+                    layout.as_ref(),
+                    item,
+                    self.repr().map(|repr| ctx.resolve_type(repr)),
+                    true,
+                ),
+            )
+        } else {
+            EnumRepr::Other(handle_translation(
+                ctx,
+                layout.as_ref(),
+                item,
+                self.repr().map(|repr| ctx.resolve_type(repr)),
+                false,
+            ))
         };
-
         let mut attrs = vec![];
 
         // TODO(emilio): Delegate this to the builders?
         match variation {
-            EnumVariation::Rust { non_exhaustive } => {
+            EnumVariation::Rust { non_exhaustive, .. } => {
                 if non_exhaustive &&
                     ctx.options().rust_features().non_exhaustive
                 {
@@ -3767,7 +4000,6 @@ impl CodeGenerator for Enum {
             });
         }
 
-        let repr = repr.to_rust_ty_or_opaque(ctx, item);
         let has_typedef = ctx.is_enum_typedef_combo(item.id());
 
         ctx.options().for_each_callback(|cb| {
diff --git a/bindgen/ir/enum_ty.rs b/bindgen/ir/enum_ty.rs
index 9b08da3bce..545c73b409 100644
--- a/bindgen/ir/enum_ty.rs
+++ b/bindgen/ir/enum_ty.rs
@@ -9,6 +9,74 @@ use crate::ir::annotations::Annotations;
 use crate::parse::ParseError;
 use crate::regex_set::RegexSet;
 
+use std::fmt::{self, Display};
+use std::ops::Deref;
+use std::str::FromStr;
+
+/// Represents option for rustified enum generation.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum RustEnumOption {
+    /// Add non-exhaustive attribute to Rust enum.
+    NonExhaustive,
+    /// Add safe `TryFrom` conversion from integer value.
+    TryFromRaw,
+    /// Provide an unsafe wrapper for transmute from integer value.
+    FromRawUnchecked,
+}
+
+impl FromStr for RustEnumOption {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "non_exhaustive" => Ok(Self::NonExhaustive),
+            "try_from_raw" => Ok(Self::TryFromRaw),
+            "from_raw_unchecked" => Ok(Self::FromRawUnchecked),
+            _ => Err(format!(
+                "Invalid or unknown rustified struct option {s:?}",
+            )),
+        }
+    }
+}
+
+/// Collection of `RustEnumOption` values.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct RustEnumOptions(Vec<RustEnumOption>);
+
+impl Deref for RustEnumOptions {
+    type Target = Vec<RustEnumOption>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl FromStr for RustEnumOptions {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(RustEnumOptions(
+            s.split(',').filter(|s| s != &"").try_fold(
+                Vec::new(),
+                |mut vec, opt| {
+                    vec.push(RustEnumOption::from_str(opt)?);
+                    Result::<_, String>::Ok(vec)
+                },
+            )?,
+        ))
+    }
+}
+
+impl Display for RustEnumOption {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            RustEnumOption::NonExhaustive => write!(f, "non_exhaustive"),
+            RustEnumOption::TryFromRaw => write!(f, "try_from_raw"),
+            RustEnumOption::FromRawUnchecked => write!(f, "from_raw_unchecked"),
+        }
+    }
+}
+
 /// An enum representing custom handling that can be given to a variant.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum EnumVariantCustomBehavior {
@@ -209,22 +277,6 @@ impl Enum {
                 is_bitfield: false,
                 is_global: true,
             }
-        } else if self.is_matching_enum(
-            ctx,
-            &ctx.options().rustified_enums,
-            item,
-        ) {
-            EnumVariation::Rust {
-                non_exhaustive: false,
-            }
-        } else if self.is_matching_enum(
-            ctx,
-            &ctx.options().rustified_non_exhaustive_enums,
-            item,
-        ) {
-            EnumVariation::Rust {
-                non_exhaustive: true,
-            }
         } else if self.is_matching_enum(
             ctx,
             &ctx.options().constified_enums,
@@ -232,7 +284,22 @@ impl Enum {
         ) {
             EnumVariation::Consts
         } else {
-            ctx.options().default_enum_style
+            let matches = ctx
+                .options()
+                .rustified_enums
+                .iter()
+                .find(|(_, regex)| self.is_matching_enum(ctx, regex, item));
+            match matches {
+                Some((options, _)) => EnumVariation::Rust {
+                    non_exhaustive: options
+                        .contains(&RustEnumOption::NonExhaustive),
+                    safe_conversion: options
+                        .contains(&RustEnumOption::TryFromRaw),
+                    unsafe_conversion: options
+                        .contains(&RustEnumOption::FromRawUnchecked),
+                },
+                None => ctx.options().default_enum_style,
+            }
         }
     }
 }
diff --git a/bindgen/lib.rs b/bindgen/lib.rs
index 1a15d51d67..637522fefd 100644
--- a/bindgen/lib.rs
+++ b/bindgen/lib.rs
@@ -52,6 +52,7 @@ pub use codegen::{
 };
 pub use features::{RustEdition, RustTarget, LATEST_STABLE_RUST};
 pub use ir::annotations::FieldVisibilityKind;
+pub use ir::enum_ty::RustEnumOptions;
 pub use ir::function::Abi;
 #[cfg(feature = "__cli")]
 pub use options::cli::builder_from_flags;
@@ -469,7 +470,7 @@ impl Builder {
 
 impl BindgenOptions {
     fn build(&mut self) {
-        const REGEX_SETS_LEN: usize = 29;
+        const REGEX_SETS_LEN: usize = 27;
 
         let regex_sets: [_; REGEX_SETS_LEN] = [
             &mut self.blocklisted_types,
@@ -488,8 +489,6 @@ impl BindgenOptions {
             &mut self.constified_enum_modules,
             &mut self.newtype_enums,
             &mut self.newtype_global_enums,
-            &mut self.rustified_enums,
-            &mut self.rustified_non_exhaustive_enums,
             &mut self.type_alias,
             &mut self.new_type_alias,
             &mut self.new_type_alias_deref,
@@ -506,7 +505,9 @@ impl BindgenOptions {
         let record_matches = self.record_matches;
         #[cfg(feature = "experimental")]
         {
-            let sets_len = REGEX_SETS_LEN + self.abi_overrides.len();
+            let sets_len = REGEX_SETS_LEN +
+                self.abi_overrides.len() +
+                self.rustified_enums.len();
             let names = if self.emit_diagnostics {
                 <[&str; REGEX_SETS_LEN]>::into_iter([
                     "--blocklist-type",
@@ -523,8 +524,6 @@ impl BindgenOptions {
                     "--bitfield-enum",
                     "--newtype-enum",
                     "--newtype-global-enum",
-                    "--rustified-enum",
-                    "--rustified-enum-non-exhaustive",
                     "--constified-enum-module",
                     "--constified-enum",
                     "--type-alias",
@@ -540,6 +539,9 @@ impl BindgenOptions {
                     "--must-use",
                 ])
                 .chain((0..self.abi_overrides.len()).map(|_| "--override-abi"))
+                .chain(
+                    (0..self.rustified_enums.len()).map(|_| "--rustified-enum"),
+                )
                 .map(Some)
                 .collect()
             } else {
@@ -556,6 +558,10 @@ impl BindgenOptions {
         for regex_set in self.abi_overrides.values_mut().chain(regex_sets) {
             regex_set.build(record_matches);
         }
+
+        for regex_set in self.rustified_enums.values_mut() {
+            regex_set.build(record_matches);
+        }
     }
 
     /// Update rust target version
diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs
index f9a8572976..ee2f3404ea 100644
--- a/bindgen/options/cli.rs
+++ b/bindgen/options/cli.rs
@@ -9,7 +9,7 @@ use crate::{
     regex_set::RegexSet,
     Abi, AliasVariation, Builder, CodegenConfig, EnumVariation,
     FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle,
-    RustTarget,
+    RustEnumOptions, RustTarget,
 };
 use clap::{
     error::{Error, ErrorKind},
@@ -137,6 +137,21 @@ fn parse_custom_attribute(
     Ok((attributes, regex.to_owned()))
 }
 
+fn parse_rustified_enum(
+    rustified_enum: &str,
+) -> Result<(RustEnumOptions, String), Error> {
+    let (regex, options) = match rustified_enum.rsplit_once('=') {
+        Some((regex, options)) => (regex, options),
+        None => (rustified_enum, ""),
+    };
+
+    let options = options
+        .parse()
+        .map_err(|err| Error::raw(ErrorKind::InvalidValue, err))?;
+
+    Ok((options, regex.to_owned()))
+}
+
 #[derive(Parser, Debug)]
 #[clap(
     about = "Generates Rust bindings from C/C++ headers.",
@@ -162,12 +177,11 @@ struct BindgenCommand {
     /// Mark any enum whose name matches REGEX as a global newtype.
     #[arg(long, value_name = "REGEX")]
     newtype_global_enum: Vec<String>,
-    /// Mark any enum whose name matches REGEX as a Rust enum.
-    #[arg(long, value_name = "REGEX")]
-    rustified_enum: Vec<String>,
-    /// Mark any enum whose name matches REGEX as a non-exhaustive Rust enum.
-    #[arg(long, value_name = "REGEX")]
-    rustified_non_exhaustive_enum: Vec<String>,
+    /// Mark any enum whose name matches the provided regex as a Rust enum. This parameter takes
+    /// options in the shape REGEX or REGEX=OPTIONS where OPTIONS can be a comma separated list of
+    /// options from non_exhaustive, try_from_raw, and from_raw_unchecked.
+    #[arg(long, value_parser = parse_rustified_enum)]
+    rustified_enum: Vec<(RustEnumOptions, String)>,
     /// Mark any enum whose name matches REGEX as a series of constants.
     #[arg(long, value_name = "REGEX")]
     constified_enum: Vec<String>,
@@ -544,7 +558,6 @@ where
         newtype_enum,
         newtype_global_enum,
         rustified_enum,
-        rustified_non_exhaustive_enum,
         constified_enum,
         constified_enum_module,
         default_macro_constant_type,
@@ -848,8 +861,7 @@ where
             bitfield_enum,
             newtype_enum,
             newtype_global_enum,
-            rustified_enum,
-            rustified_non_exhaustive_enum,
+            rustified_enum => |b, (rustified_enum, regex)| b.rustified_enum(rustified_enum, regex),
             constified_enum,
             constified_enum_module,
             default_macro_constant_type,
diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs
index ab6b232ec3..140f5b20f0 100644
--- a/bindgen/options/mod.rs
+++ b/bindgen/options/mod.rs
@@ -20,6 +20,7 @@ use crate::CodegenConfig;
 use crate::FieldVisibilityKind;
 use crate::Formatter;
 use crate::HashMap;
+use crate::RustEnumOptions;
 use crate::DEFAULT_ANON_FIELDS_PREFIX;
 
 use std::env;
@@ -496,7 +497,7 @@ options! {
         as_args: "--newtype-global-enum",
     },
     /// `enum`s marked as Rust `enum`s.
-    rustified_enums: RegexSet {
+    rustified_enums: HashMap<RustEnumOptions, RegexSet> {
         methods: {
             regex_option! {
                 /// Mark the given `enum` as a Rust `enum`.
@@ -507,13 +508,21 @@ options! {
                 /// **Use this with caution**, creating an instance of a Rust `enum` with an
                 /// invalid value will cause undefined behaviour. To avoid this, use the
                 /// [`Builder::newtype_enum`] style instead.
-                pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
-                    self.options.rustified_enums.insert(arg);
+                pub fn rustified_enum<T: Into<String>>(mut self, options: RustEnumOptions, arg: T) -> Builder {
+                    self.options.rustified_enums.entry(options).or_default().insert(arg.into());
                     self
                 }
             }
         },
-        as_args: "--rustified-enum",
+        as_args: |overrides, args| {
+            for (options, set) in overrides {
+                let options = options.iter().map(|item| item.to_string()).collect::<Vec<_>>();
+                for item in set.get_items() {
+                    args.push("--rustified-enum".to_owned());
+                    args.push(format!("{}={}", item, options.join(",")));
+                }
+            }
+        },
     },
     /// `enum`s marked as non-exhaustive Rust `enum`s.
     rustified_non_exhaustive_enums: RegexSet {