Rust std::mem::transmute实例讲解

2023-05-16

Rust std::mem::transmute实例讲解
时间:2022-04-08

本文章向大家介绍Rust std::mem::transmute实例讲解,主要分析其语法、参数、返回值和注意事项,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

用法

pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(e:T) -> U

将一种类型的值的位重新解释为另一种类型。
两种类型必须具有相同的大小。原始值和结果都不是无效值。
transmute 在语义上等同于将一种类型按位移动到另一种类型。它将源值中的位复制到目标值中,然后忘记原始值。它相当于 C 的 memcpy ,就像 transmute_copy 一样。
因为transmute是 by-value 操作,转换值本身的对齐不是问题。与任何其他函数一样,编译器已经确保T和U正确对齐。但是,当转换值时指向别处(例如指针、引用、框…),调用者必须确保正确对齐指向的值。
transmute 非常不安全。有很多方法可以使用此函数导致未定义的行为。 transmute 应该是绝对的最后手段。

在const 上下文中转换指向整数的指针是未定义的行为。任何将结果值用于整数运算的尝试都将中止const-evaluation。

nomicon 有额外的文档。
例子
transmute 在某些方面非常有用。
将指针转换为函数指针。这对于函数指针和数据指针具有不同大小的机器是不可移植的。

fn foo() -> i32 {
    0
}
let pointer = foo as *const ();
let function = unsafe {
    std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);

延长寿命,或缩短不变的寿命。这是高级的,非常不安全的 Rust!

struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r:R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r:&'b mut R<'static>)
                                             -> &'b mut R<'c> {
    std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}

备择方案

不要绝望:transmute 的许多用途可以通过其他方式实现。以下是transmute 的常见应用,可以用更安全的构造替换。

将原始字节(&[u8])转换为 u32、f64 等:

let raw_bytes = [0x78, 0x56, 0x34, 0x12];

let num = unsafe {
    std::mem::transmute::<[u8; 4], u32>(raw_bytes)
};

// use `u32::from_ne_bytes` instead
let num = u32::from_ne_bytes(raw_bytes);
// or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);

将指针变成 usize :

let ptr = &0;
let ptr_num_transmute = unsafe {
    std::mem::transmute::<&i32, usize>(ptr)
};

// Use an `as` cast instead
let ptr_num_cast = ptr as *const i32 as usize;

将 *mut T 变成 &mut T :

let ptr:*mut i32 = &mut 0;
let ref_transmuted = unsafe {
    std::mem::transmute::<*mut i32, &mut i32>(ptr)
};

// Use a reborrow instead
let ref_casted = unsafe { &mut *ptr };

将 &mut T 变成 &mut U :

let ptr = &mut 0;
let val_transmuted = unsafe {
    std::mem::transmute::<&mut i32, &mut u32>(ptr)
};

// Now, put together `as` and reborrowing - note the chaining of `as`
// `as` is not transitive
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };

将 &str 变成 &[u8] :

// this is not a good way to do this.
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);

// You could use `str::as_bytes`
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);

// Or, just use a byte string, if you have control over the string
// literal
assert_eq!(b"Rust", &[82, 117, 115, 116]);

将 Vec<&T> 变成 Vec<Option<&T>> 。

要转换容器内容的内部类型,您必须确保不违反任何容器的不变量。对于 Vec ,这意味着内部类型的大小和对齐方式都必须匹配。其他容器可能依赖于类型的大小、对齐方式,甚至是 TypeId ,在这种情况下,如果不违反容器不变量,就根本不可能进行转换。

let store = [0, 1, 2, 3];
let v_orig = store.iter().collect::<Vec<&i32>>();

// clone the vector as we will reuse them later
let v_clone = v_orig.clone();

// Using transmute:this relies on the unspecified data layout of `Vec`, which is a
// bad idea and could cause Undefined Behavior.
// However, it is no-copy.
let v_transmuted = unsafe {
    std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_clone)
};

let v_clone = v_orig.clone();

// This is the suggested, safe way.
// It does copy the entire vector, though, into a new array.
let v_collected = v_clone.into_iter()
                         .map(Some)
                         .collect::<Vec<Option<&i32>>>();

let v_clone = v_orig.clone();

// This is the proper no-copy, unsafe way of "transmuting" a `Vec`, without relying on the
// data layout. Instead of literally calling `transmute`, we perform a pointer cast, but
// in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`),
// this has all the same caveats. Besides the information provided above, also consult the
// [`from_raw_parts`] documentation.
let v_from_raw = unsafe {
    // Ensure the original vector is not dropped.
    let mut v_clone = std::mem::ManuallyDrop::new(v_clone);
    Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>,
                        v_clone.len(),
                        v_clone.capacity())
};

实施 split_at_mut :

use std::{slice, mem};

// There are multiple ways to do this, and there are multiple problems
// with the following (transmute) way.
fn split_at_mut_transmute<T>(slice:&mut [T], mid:usize)
                             -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice);
        // first:transmute is not type safe; all it checks is that T and
        // U are of the same size. Second, right here, you have two
        // mutable references pointing to the same memory.
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// This gets rid of the type safety problems; `&mut *` will *only* give
// you an `&mut T` from an `&mut T` or `*mut T`.
fn split_at_mut_casts<T>(slice:&mut [T], mid:usize)
                         -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = &mut *(slice as *mut [T]);
        // however, you still have two mutable references pointing to
        // the same memory.
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// This is how the standard library does it. This is the best method, if
// you need to do something like this
fn split_at_stdlib<T>(slice:&mut [T], mid:usize)
                      -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let ptr = slice.as_mut_ptr();
        // This now has three mutable references pointing at the same
        // memory. `slice`, the rvalue ret.0, and the rvalue ret.1.
        // `slice` is never used after `let ptr = ...`, and so one can
        // treat it as "dead", and therefore, you only have two real
        // mutable slices.
        (slice::from_raw_parts_mut(ptr, mid),
         slice::from_raw_parts_mut(ptr.add(mid), len - mid))
    }
}

关于trait object:


use std::fmt::{Debug, Display};
use std::mem::transmute;

fn main() {
    let s1 = String::from("hello world!");
    let s2 = String::from("goodbye world!");
    // Display / Debug trait object for s
    let w1: &dyn Display = &s1;
    let w2: &dyn Debug = &s1;

    // Display / Debug trait object for s1
    let w3: &dyn Display = &s2;
    let w4: &dyn Debug = &s2;

    // 强行把 triat object 转换成两个地址 (usize, usize)
    // 这是不安全的,所以是 unsafe
    let (addr1, vtable1): (usize, usize) = unsafe { transmute(w1) };
    let (addr2, vtable2): (usize, usize) = unsafe { transmute(w2) };
    let (addr3, vtable3): (usize, usize) = unsafe { transmute(w3) };
    let (addr4, vtable4): (usize, usize) = unsafe { transmute(w4) };

    // s 和 s1 在栈上的地址,以及 main 在 TEXT 段的地址
    println!(
        "s1: {:p}, s2: {:p}, main(): {:p}",
        &s1, &s2, main as *const ()
    );
    // trait object(s / Display) 的 ptr 地址和 vtable 地址
    println!("addr1: 0x{:x}, vtable1: 0x{:x}", addr1, vtable1);
    // trait object(s / Debug) 的 ptr 地址和 vtable 地址
    println!("addr2: 0x{:x}, vtable2: 0x{:x}", addr2, vtable2);

    // trait object(s1 / Display) 的 ptr 地址和 vtable 地址
    println!("addr3: 0x{:x}, vtable3: 0x{:x}", addr3, vtable3);

    // trait object(s1 / Display) 的 ptr 地址和 vtable 地址
    println!("addr4: 0x{:x}, vtable4: 0x{:x}", addr4, vtable4);

    // 指向同一个数据的 trait object 其 ptr 地址相同
    assert_eq!(addr1, addr2);
    assert_eq!(addr3, addr4);

    // 指向同一种类型的同一个 trait 的 vtable 地址相同
    // 这里都是 String + Display
    assert_eq!(vtable1, vtable3);
    // 这里都是 String + Debug
    assert_eq!(vtable2, vtable4);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rust std::mem::transmute实例讲解 的相关文章

  • CSP考试 2016年04月第3题 路径解析 C++实现

    表示本目录 xff0c 例如 d1 f1 指定的就是 d1 f1 如果有多个连续的 出现 xff0c 其效果等同于一个 绝对路径 xff1a 以 符号开头 xff0c 表示从根目录开始构建的路径 相对路径 xff1a 不以 符号开头 xff
  • bash环境中的通配符和特殊符号的简单介绍

    Bash环境中可以用通配符 wildcard 来更好的对数据进行处理 下面介绍一些常用的通配符 符号 意义 代表0个到无穷多个任意字符 代表一定有一个任意字符 代表一定有一个在中括号内得字符 非任意字符 例如 abcd 代表一定有一个字符
  • spring boot自定义注解拦截接口

    自定义注解 xff0c 拦截接口请求 maven依赖管理 span class token generics function span class token punctuation lt span dependency span cla
  • nacos与spring cloud

    前言 从微服务兴起后 xff0c 服务治理问题已经成为其最大问题 起初有eureka xff0c zookeeper consul同台竞争 xff0c 本人也使用过eureka 43 config server作为注册中心和配置中心 xff
  • nacos与sentinel

    sentinel 高可用流控 sentinel 是阿里开源的一款系统流控系统 xff0c 可以在线配置本系统请求访问请求控制 软件下载 源码 运行版 运行 nohup java jar sentinel dashboard 1 7 2 ja
  • jenkins 结合ssh 实现多服务器部署jar包

    jenkins使用手册 简介 官网介绍 xff1a Jenkins是开源的CI CD xff08 持续集成 持续交付 xff09 软件领导者 有如下几个优点 xff1a 简单安装配置简单丰富的插件 超过1000个插件扩展性分布式 一 软件下
  • feign调用初始化问题

    最近使用spring cloud微服务遇到一个问题 xff0c 微服务之间使feign第一次调用时会非常的耗时 xff0c 一个请求如果调用链长的情况下 xff0c 接口返回可能会达到10s以上 xff0c 这是一个正常系统都不能容忍的 基
  • VScode 无法下载Go工具解决方案

    使用七牛云代理下载所需要的工具依赖 xff0c 具体配置请查看 说明 https goproxy cn
  • Sonar代码质量管理服务搭建并导入java项目

    1 软件下载 7 4 2 软件配置 打开解压后文件conf sonar properties mysql版本必须大于5 6小于8 0 MySQL gt 61 5 6 amp amp lt 8 0 sonar jdbc url 61 mysq
  • java join方法实现线程的串行

    java实现多线程之间串行执行 xff0c 网上也有很多的教程 在这里我主要说的是 xff1a java Thread类的join函数 xff0c 先贴代码吧 xff1a package cn com fhz thread Created
  • Windows上Rust所依赖的msvc到底怎么装?

    在Windows上面安装Rust的开发环境 xff0c 看起来颇具挑战性 我们会被告知需要安装一个名叫Microsoft Visual Studio C 43 43 build tools的编译工具 xff0c 并被给到一个官方链接 然而
  • Word 转 Markdown

    1 Pandoc 工具将 Word 文档转为 Markdown 可以借助 Pandoc 工具将 Word 文档转为 Markdown xff0c 例如 xff0c 此处将 README docx 转成 README md xff0c 命令如
  • c语言现代方法 chapter20自学笔记

    如果编写程序需要用到数据在内存中如何存储 xff0c 那么除非必要 xff0c 否则不用 xff0c 如果用 xff0c 那么集中在程序中的某个模块 xff0c 不要分散在各处 20 1 移位运算符 c语言提供了6个位运算符 这些运算符可以
  • 查看python源码之jieba安装

    Python 2 x 下的安装 全自动安装 xff1a easy install jieba 或者 pip install jieba 半自动安装 xff1a 先下载http pypi python org pypi jieba xff0c
  • 在Ubuntu Linux上搭建go语言环境

    一 安装VMware Tools 1 在刚装好的ubuntu linux上 xff0c 如果没有安装VMware Tools xff0c 那么我们就要先安装它 打开我们的ubuntu linux xff0c 然后点击左上角的虚拟机 xff0
  • Linux nohup实现后台运行程序及查看(nohup与&)

    1 后台执行 一般运行linux上的程序都是执行 sh 文件 xff08 sh文件 xff09 xff0c 那如果不影响当前CMD窗口的操作 xff0c 需要后台运行怎么办呢 xff1f 这时就需要借助 nohup 和 amp 命令来实现
  • CA 认证过程及 https 实现方法

    CA 认证过程 CA 概述 xff1a Certificate Authority 的缩写 xff0c 通常翻译成认证权威或者认证中心 xff0c 主要用途是为用户发放数字证书 CA 认证的流程和公安局派出所颁发身份证的流程一样 认证中心
  • C++代码详解:string的赋值与C风格字符串转换

    C 43 43 代码详解 string的赋值与C风格字符串转换 61 61 61 61 61 string简介 61 61 61 61 61 string是C 43 43 里默认的字符串容器 xff0c 用于代替C风格的字符串指针与字符串数
  • nginx 基本配置

    一篇比较好的参考文 https www digitalocean com community tutorials how to install nginx on ubuntu 18 04 1 在 Ubuntu 上安装 Nginx sudo
  • 解决[[NSFileManager defaultManager] contentsOfDirectoryAtPath 方法获取不到数据的bug

    在说这个问题之前 必须先解释一下 我们在引入工程的时候 xcode会给我们3个选项 1 Copy items if needed 主要是说明 xff0c 是否要将文件拷贝到工程所在目录 如果不选中 xff0c 而且该库文件不在工程目录下 x

随机推荐

  • GitLab+Jenkins集群+docker CICD集成

    前言 最近部门进行CICD架构升级将引入k8s编排docker容器 借此机会梳理下目前应用部署发布方式 当前架构是我刚到公司时基于gitlab 43 jenkins 43 docker 43 nexus搭建 引入K8S后将调整pod yam
  • 10个轻松上手制作的Arduino项目

    创建Arduino项目可以给您带来很大的满足感 xff0c 但很多时候初学者不知道从哪里开始 启动创建项目时需要考虑很多事情 xff0c 如果您没有制作的经验 xff0c 那可能会令人困惑 正是因为这个原因 xff0c 我们为初学者收集到1
  • 7个基于Arduino的神奇项目!

    创客人数的规模正在增加 xff0c 从当地的超市到大城市繁忙的办公室 xff0c 到处都可以找到他们 xff0c 这些地方可能会有一个或两个创客 xff0c 或许更多 xff0c 在制定他们的下一个设计 xff0c 或者可能渴望找到一个前现
  • 用if语句如何检测一个变量是否存在。

    例如我们用if语句检测一下num这个变量是否存在 若我们直接写 xff1a var night 61 34 34 if num night 61 34 yes 34 console log night 注意 xff01 xff01 1 如果
  • nodejs中的__filename和__dirname的使用说明

    在node js开发中 xff0c 有时候需要获取一些环境变量 xff0c 其中 filename和 dirname 是两个有用的环境变量 dirname 获取的是调用 dirname的脚本文件本身的绝对路径 xff0c 不是启动脚本文件的
  • LPC1768 -- RTC实时时钟

    RTC是当下设备中比较普遍的一个部件 xff0c 很多设备都需要查看时间 RTC实时时钟已经在很多的单片机中集成 xff0c 以前还要专门的时钟芯片 xff0c 现在Cortex M3内核都包括了这个部件了 和以前NXP的ARM7内核不同的
  • Ubuntu Apache2配置SSL证书

    一 Ubuntu下的Apache2 1 安装Apache2 sudo apt get install apache2 默认站点在 var www 配置文件在 etc apache2 日志在 var log apache 启动脚本是 etc
  • 时间机器

    让我们回到过去的是回忆 让我们勇敢前行的是期望 致我最爱的电影 时间机器
  • pyqt5 Qthread事件 进度条 案例

    pyqt5 Qthread事件 进度条 案例 代码 xff1a from PyQt5 import QtWidgets span class token punctuation span QtCore import sys from PyQ
  • CString 的成员函数详解

    CString 的成员函数详解 CSTRING的成员函数详解 typedef ATL CStringT lt TCHAR StrTraitMFC DLL lt TCHAR gt gt CString CString的构造函数 xff1a 1
  • Rust: Rust 异步入门 (作者洋芋,来自Rust语言中文社区)

    Rust每周一知 Rust 异步入门 原创 洋芋 Rust语言中文社区 前天 这是一篇博文翻译 xff0c 略有删减 xff0c 整理代码方便统一阅读 xff0c Github链接 xff1a https github com lester
  • (转)Julia PkgServer 镜像服务

    https mp weixin qq com s DyegFcNEjieJspc 3B5G6w Julia PkgServer 镜像服务 原创 Johnny Chen JuliaCN 昨天 长久以来 xff0c 受国内网络环境影响 xff0
  • python : pandas库的后继者polars库

    polars库是python的又一dataframe库 xff0c 显然 xff0c 在pandas库的光芒下 xff0c 要上位是不容易的 xff0c 必须有过硬的功夫 一 用法基本一致 从长相上看 xff0c 两者有孪生相 xff0c
  • 证券类私募主要需求及核心服务商

  • Python:同花顺全数据接口

    前往 xff1a http quantapi 10jqka com cn page 61 home 如果是windows 根据不同用户 xff0c 可以选择不同的下载 1 iFinDPy 模块 下载完成后 xff0c 会出现一个DataIn
  • 使用Python中PIL图形库进行截屏

    目的 xff1a 通过使用Python的一个图形库PIL xff08 Python Image Library xff09 对屏幕进行截图 步骤 xff1a 1 下载PIL xff08 路径 xff09 并安装 2 新建文件 截屏 py x
  • 详解Python中pyautogui库的最全使用方法

    详解Python中pyautogui库的最全使用方法 在使用Python做脚本的话 xff0c 有两个库可以使用 xff0c 一个为PyUserInput库 xff0c 另一个为pyautogui库 就本人而言 xff0c 我更喜欢使用py
  • python : pandas 中多重索引multiindex与多个标的dataframe

    多个标的dataframe 如何转成多重索引的dataframe 有点象 xff0c 有中证500股票的数据 xff0c 如何把这500只股票 xff0c 整成一个多重索引的dataframe span class token functi
  • 关于Rust中的自引用:差之毫厘?!

    先设计一个自引用类型 xff0c 然后能过std mem swap来观察其变化 xff1a 一 为什么自引用没有出现预期的问题 use std mem derive Debug struct SelfRef name String ptr
  • Rust std::mem::transmute实例讲解

    Rust std mem transmute实例讲解 时间 2022 04 08 本文章向大家介绍Rust std mem transmute实例讲解 xff0c 主要分析其语法 参数 返回值和注意事项 xff0c 并结合实例形式分析了其使