Rust, Eclipse и STM32

  • Tutorial
Для того, чтобы подружить между собой указанные в заголовке технологии нам понадобятся:


Идея состоит в том, чтобы с скомпилировать написанную на Rust программу в библиотеку, которую можно будет слинковать с помощью тулчейна для ARM.
В итоге мы сможем даже вполне комфортно дебажить смешанный код на Rust и С.

1. Генерация проекта на C


Воспользуемся для этого утилитой STM32CubeMX. Для демо-проекта нам понадобится:

  • SYS = Serial Wire (если у устройство подключено по SWD) либо JTAG
  • USART2 в конфигурации Asynchronous
  • Несколько пинов на одном порту в режиме GPIO_Output (назовем их LED_R, LED_G, LED_B)

image

Проверим настройки тактирования. Тут при желании можем указать тактирование от внешнего кварца и его частоту.

image

Сгенерируем проект. Назовем его “HwApi”, т.к. этот слой кода у нас будет представлять собой абстракцию над железом, который мы будем использовать при написании кода на Rust. В качестве IDE выбираем SW4STM32.

image

Если Workbench установлен, то можем открыть сгенерированный проект и проверить, что он успешно компилируется.

image

2. Создаем проект для свежей версии Eclipse


Хоть System Workbench и основан на Eclipse, нам придется создать новый проект в свежей мажорной версии Eclipse (Neon), т.к. RustDT несовместим с той версией Eclipse.

Также нам понадобится шаблон проекта, который устанавливается вместе с GNU ARM Eclipse Plugin.

image

Для того, чтобы успешно слинковать либу, сгенерированную rust компилятором, нам понадобится заранее установленная свежая версия GNU ARM Embedded Toolchain.

image

Начинаем процесс переноса проекта из System Workbench в Eclipse CDT. В интернете можно найти скрипты, которые этот процесс автоматизируют, но я буду это делать вручную, т.к. собираюсь переиспользовать HwApiLib в других проектах, изменяя только написанную на Rust часть кода.
Копируем следующие папки/файлы в новый проект:

  • Drivers
  • Inc
  • Src
  • startup
  • STM32F103C8Tx_FLASH.ld

Если Workbench установлен, то разворачиваем два окна настроек проектов (из старого и нового Eclipse) так, чтобы было удобно копировать значения из одного окна в другое. Окна немного отличаются, поэтому при копировании ориентируемся на флаги, которые указаны в скобках.

Если Workbench не установлен, можно просто скопировать настройки со скриншотов, приложенных ниже.

Копируем Defined Symbols:

image

Пути к папкам, содержащие *.h файлы:

image

На вкладке “Optimization” можно включить оптимизацию Optimize size(-Os).
Далее указываем, что нам нужны все предупреждения компилятора:

image

Указываем путь к скрипту линкера + отмечаем чекбокс для удаления из результата линковки неиспользуемых в коде секций:

image

На следующей вкладке важно отметить чекбокс “Use newlib-nano” и вручную указать флаг -specs=nosys.specs:

image

Указываем пути к папкам с файлами для компиляции:

image

Нажимаем Ок. После чего меняем расширение startup файла на заглавную .S, чтобы файл успешно подхватился компилятором. Проверяем, что проект компилируется.

image

Теперь нужно настроить дебаггер (Run — Debug Configurations — GDB OpenOCD Debugging). Создаем файл для OpenOCD с описанием железа, в котором будет запускаться программа (в моем случае файл называется STM32F103C8x_SWD.cfg):

source [find interface/stlink-v2.cfg]

set WORKAREASIZE 0x5000
transport select "hla_swd"
set CHIPNAME STM32F103C8Tx

source [find target/stm32f1x.cfg]

# use hardware reset, connect under reset
reset_config none

Если у вас используется другой микроконтроллер или другой способ подключения к нему, то корректный файл для OpenOCD можно сгенерировать в Workbench (с помощью Debugging options — Ac6).

В Config options указываем флаг -f и путь к созданному в предыдущем шаге файлу.

image

Жмем Debug. Проверяем, что дебаггер успешно залил код в микроконтроллер и началась отладка.

image

Пришло время создавать Rust проект. Т.к. нам понадобятся инструкции компилятора, которые не поддерживаются в stable версии, нам нужно будет переключиться нам nightly версию компилятора, запустив в cmd следующие команды:

rustup update
rustup default nightly

Далее нужно получить текущую версию компилятора:

rustc -v --version

image

Затем склонировать себе исходники rust и переключится на коммит, который использовался для сборки этого компилятора (указан в commit-hash).

git clone git@github.com:rust-lang/rust.git
cd rust
git checkout cab4bff3de1a61472f3c2e7752ef54b87344d1c9

Следующим шагом скомпилируем необходимые нам библиотеки под ARM.

mkdir libs-arm
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libcore/lib.rs --out-dir libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/liballoc/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libstd_unicode/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libcollections/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link

В будущем, при каждом обновлении компилятора (rustup update) нужно будет переключаться на актуальную версию исходников и перекомпилировать библиотеки для ARM, иначе потеряется возможность дебажить код на rust.

Наконец-то можно приступить к создают Rust-проекта в eclipse.

image

image

Eclipse просит указать путь к компилятору, исходникам и утилитам для работы с rust-кодом.

image

Обычно эти компоненты можно найти в C:\Users\%username%\.cargo. Rust src — путь к папке src в исходниках, которые мы скачали ранее.

Теперь основной код:

lib.rs

#![feature(macro_reexport)]
#![feature(unboxed_closures)]
#![feature(lang_items, asm)]

#![no_std]
#![feature(alloc, collections)]

#![allow(dead_code)]
#![allow(non_snake_case)]


extern crate alloc;

pub mod runtime_support;
pub mod api;

#[macro_reexport(vec, format)]
pub extern crate collections;

use api::*;


#[no_mangle]
pub extern fn demo_main_loop() -> ! {
	let usart2 = Stm32Usart::new(Stm32UsartDevice::Usart2);

	loop { 

		let u2_byte = usart2.try_read_byte();
		match u2_byte {
			Some(v) => {
				let c = v as char;
				 match c {
					'r' => { toggle_led(Stm32Led::Red); }
					'g' => { toggle_led(Stm32Led::Green); }
					'b' => { toggle_led(Stm32Led::Blue); } 
					_ => { usart2.print("cmd not found"); }
				}		
			}
			_ => {}
		}

		delay(1);
	}
}

api.rs — прослойка для интеграции между собой Rust и C кода

use collections::Vec;

extern {
	fn stm32_delay(millis: u32);
	fn usart2_send_string(str: *const u8, len: u16);
	fn usart2_send_byte(byte: u8);
	fn usart2_try_get_byte() -> i16;
	fn stm32_toggle_led(led: u8);
	fn stm32_enable_led(led: u8);
	fn stm32_disable_led(led: u8);
}

pub fn delay(millis: u32) {
	unsafe {
		stm32_delay(millis);
	}
}


#[derive(Copy, Clone)]
pub enum Stm32UsartDevice {
	Usart2
}

#[derive(Copy, Clone)]
pub struct Stm32Usart {
	device: Stm32UsartDevice
}


impl Stm32Usart {
	pub fn new(device: Stm32UsartDevice) -> Stm32Usart {
		Stm32Usart {
			device: device
		}
	}

	pub fn print(&self, str: &str) {
		let bytes = str.bytes().collect::<Vec<u8>>();
		self.print_bytes(bytes.as_slice());
	}

	pub fn print_bytes(&self, bytes: &[u8]) {
		unsafe {
			match self.device {
				Stm32UsartDevice::Usart2 => usart2_send_string(bytes.as_ptr(), bytes.len() as u16)
			}
		}
	}

	pub fn println(&self, str: &str) {
		self.print(str);
		self.print("\r\n");
	}

	pub fn send_byte(&self, byte: u8) {
		unsafe {
			match self.device {
				Stm32UsartDevice::Usart2 => usart2_send_byte(byte)
			}
		}
	}

	pub fn try_read_byte(&self) -> Option<u8> {
		unsafe {
			let r = usart2_try_get_byte();
			if r == -1 { return None; }
			return Some(r as u8);
		}
	}
}


pub enum Stm32Led {
	Red,
	Green,
	Blue,
	Orange
}

impl Stm32Led {
	fn to_api(&self) -> u8 {
		match *self {
			Stm32Led::Green => 2, 
			Stm32Led::Blue => 3,
			Stm32Led::Red => 1,
			Stm32Led::Orange => 0
		}
	}
}

pub fn toggle_led(led: Stm32Led) {
	unsafe {
		stm32_toggle_led(led.to_api());
	}
}

pub fn enable_led(led: Stm32Led) {
	unsafe {
		stm32_enable_led(led.to_api());
	}
}

pub fn disable_led(led: Stm32Led) {
	unsafe {
		stm32_disable_led(led.to_api());
	}
}

runtime_support.rs — для поддержки низкоуровневых функций Rust

extern crate core;

/// Call the debugger and halts execution.
#[no_mangle]
pub extern "C" fn abort() -> ! {
    loop {}
}

#[cfg(not(test))]
#[inline(always)]
/// NOP instruction
pub fn nop() {
    unsafe {
        asm!("nop" :::: "volatile");
    }
}

#[cfg(test)]
/// NOP instruction (mock)
pub fn nop() {}

#[cfg(not(test))]
#[inline(always)]
/// WFI instruction
pub fn wfi() {
    unsafe {
        asm!("wfi" :::: "volatile");
    }
}

#[cfg(test)]
/// WFI instruction (mock)
pub fn wfi() {}

#[lang = "panic_fmt"]
fn panic_fmt(_: core::fmt::Arguments, _: &(&'static str, usize)) -> ! {
    loop {}
}

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}


// Memory allocator support, via C's stdlib


#[repr(u8)]
#[allow(non_camel_case_types)]
pub enum c_void {
    __variant1,
    __variant2,
}

extern "C" {
    pub fn malloc(size: u32) -> *mut c_void;
    pub fn realloc(p: *mut c_void, size: u32) -> *mut c_void;
    pub fn free(p: *mut c_void);
}

#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
    malloc(size as u32) as *mut u8
}

#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
    free(ptr as *mut c_void);
}

#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_reallocate(ptr: *mut u8,
                                           old_size: usize,
                                           size: usize,
                                           align: usize)
                                           -> *mut u8 {
    realloc(ptr as *mut c_void, size as u32) as *mut u8
}

Также в корне проекта необходимо создать файл конфигурации целевой платформы
thumbv7m-none-eabi.json
grossws подсказал, что теперь этот файл включен в компилятор и можно его не создавать.

{
    "arch": "arm",
    "cpu": "cortex-m3",
    "data-layout": "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-v128:64:128-a:0:32-n32-S64",
    "disable-redzone": true,
    "executables": true,
    "llvm-target": "thumbv7m-none-eabi",
    "morestack": false,
    "os": "none",
    "relocation-model": "static",
    "target-endian": "little",
    "target-pointer-width": "32"
}

Копируем в папку Rust проекта папку libs-arm содержащую скомпилированные для работы под ARM компоненты из стандартной библиотеки Rust.

Изменяем Debug target, так чтобы он запускал компиляцию с нужными нам параметрами

rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g --crate-type lib -L libs-arm src/lib.rs --emit obj,link

image

image

Компилируем Rust-проект. В результате в папке проекта появится файл lib.o.

Теперь в С-проекте создаем файлы api.h/api.c, в которых объявляем и реализуем функции, которые используются в api.rs.

api.h

#ifndef SERIAL_DEMO_API_H_
#define SERIAL_DEMO_API_H_

#include "stm32f1xx_hal.h"

void stm32_delay(uint32_t milli);
void usart2_send_string(uint8_t* str, uint16_t len);
void usart2_send_byte(uint8_t byte);
int16_t usart2_try_get_byte(void);

void stm32_toggle_led(uint8_t led);
void stm32_enable_led(uint8_t led);
void stm32_disable_led(uint8_t led);

#endif

api.c

#include "api.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_uart.h"
#include "main.h"

void stm32_delay(uint32_t milli) {
	HAL_Delay(milli);
}


extern UART_HandleTypeDef huart2;

void usart2_send_string(uint8_t* str, uint16_t len) {
	HAL_UART_Transmit(&huart2, str, len, 1000);
}

void usart2_send_byte(uint8_t byte) {
	while (!(USART2->SR & UART_FLAG_TXE));
    USART2->DR = (byte & 0xFF);
}

int16_t usart2_try_get_byte(void) {
    volatile unsigned int vsr;
    vsr = USART2->SR;
    if (vsr & UART_FLAG_RXNE) {
    	USART2->SR &= ~(UART_FLAG_RXNE);
    	return (USART2->DR & 0x1FF);
    }

    return -1;
}

uint16_t stm32_led_to_pin(uint8_t led);

void stm32_toggle_led(uint8_t led) {
	HAL_GPIO_TogglePin(LED_R_GPIO_Port, stm32_led_to_pin(led));
}

void stm32_enable_led(uint8_t led) {
	HAL_GPIO_WritePin(LED_R_GPIO_Port, stm32_led_to_pin(led), GPIO_PIN_SET);
}

void stm32_disable_led(uint8_t led) {
	HAL_GPIO_WritePin(LED_R_GPIO_Port, stm32_led_to_pin(led), GPIO_PIN_RESET);
}

uint16_t stm32_led_to_pin(uint8_t led) {
	switch (led) {
		case 1:
			return LED_R_Pin;
		case 2:
			return LED_G_Pin;
		case 3:
			return LED_B_Pin;
		default:
			return LED_B_Pin;
	}
}

Добавляем вызов demo_main_loop() внутри функции main.

main.c

...
 /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
	  demo_main_loop();
  }
  /* USER CODE END 3 */
...

Осталось всё слинковать. Для этого открываем свойства проекта на C и укажем линковщику где взять недостающие obj файлы.

image

Компилируем. Бинарник сильно прибавил в весе, но все еще умещается в STM32F103C8.

image

Запускаем Debug и видим, что Eclipse без проблем переходит из C-кода в Rust.

image

В завершении статьи хочу выразить благодарность авторам следующих постов, без них я бы не осилил этот процесс:

www.hashmismatch.net/pragmatic-bare-metal-rust
spin.atomicobject.com/2015/02/20/rust-language-c-embedded
github.com/japaric/rust-cross

Статью писал с надеждой на то, что это послужит дополнительным шагом в появлении комьюнити разработчиков использующих Rust для программирования под микроконтроллеры, т.к. это действительно удобный и современный язык, несмотря на то, что у него довольно высокий порог вхождения.
Share post

Similar posts

Comments 24

    +4
    В будущем, при каждом обновлении компилятора (rustup update) нужно будет переключаться на актуальную версию исходников и перекомпилировать библиотеки для ARM, иначе потеряется возможность дебажить код на rust.

    Попробуйте xargo, чтобы не перекомпилировать нужное руками.

      +5
      Также в корне проекта необходимо создать файл конфигурации целевой платформы thumbv7m-none-eabi.json

      Стараниями Jorge Aparicio (автора xargo, cross, svd2rust и кучи других полезных вещей, а также одного из самых активных движителей развития rust в embedded) теперь должно быть можно спокойно жить без ручного создания этого файла (тем более он имел свойство ломаться при изменениях версий llvm), т. к. оно есть в комплекте с компилятором.


      Ещё рекомендую посмотреть на его же проект utest поддерживающий тестирование в qemu и под аппаратным отладчиком с использованием semihosting.

        0
        Проверил, действительно, теперь проект можно компилировать без этого файла. Спасибо!
      +6
      match u2_byte {
      Some(v) => {
          let c = v as char;
          match c {
              'r' => ...
              'g' => ...
              'b' => ...

      От преобразования u8 в char в этом коде можно избавиться, если использовать байтовые литералы b'r', b'g' и т.д. Например,


      match u2_byte {
          Some(v) => {
              match v {
                  b'r' => ...
                  b'g' => ...
                  b'b' => ...

      или


      match u2_byte {
          Some(b'r') => ...
          Some(b'g') => ...
          Some(b'b') => ...
        +4
        Кто-то использует RustDT + Eclipse кроме меня, вау!
        Для непривыкшего к файлово-конфиговой возне с vim/emacs, вроде меня, Eclipse на удивление хорошо и стабильно работает. Есть приколы с созданием exe-проекта, но в остальном, включая дебаг, вполне себе рулит.
          0

          а у вас дебаг в RustDT нормально работает? Я пробовал RustDT и сразу наткнулся на 2 бага:


          1. Если что-то поменять в программе и пытаться перекомпилить, то бывает выводится сообщение "Blocking waiting for file lock on build directory" — и компиляция не начинается
          2. если у нас есть:
            fn func(v:&i32) {
            println!("v={}", v);
            }

            мы поставили туда точку останова и стали на нее, то v в окошке переменных отображается как указатель — показывает адрес, а не значение по этому адресу. Посмотреть значение невозможно. Ну и плюс, переменная v дважды там показывается в списке. И, при выделении одной из строк — обе начинают перемигиваться.


            +2
            Что работает:
            — Пошаговая отладка, в любом потоке
            — Отображение содержимого переменных. Для enum приходится напрячь воображение, но в принципе читаемо
            — Отладка ffi (если сишная библиотека собрана с дебагом)
            Что не работает
            — Автоматический выход из процедуры (нужно нажать F7 в конце процедуры, тогда выходит нормально)
            — Отображение сложно вложенных структур и векторов (отображается только первый элемент)
            — Отладка макросов (топчется несколько тактов и перескакивает)

            Перекомпиляция и перезапуск дебага происходят без проблем.
            Windows + Rust Nightly GNU.
              0
              ясно. Я под убунтой. Не знаю, что не так, но RustDT произвел крайне плохое впечатление: глючно до такой степени, что не подходит.
          +1
          А исполняемые файлы на Rust на много жирнее получаются, по сравнению с C?
          Удается ли воспользоваться какими-нибудь готовыми библиотеками, просто указав зависимость в Cargo.toml?
            +1
            В основном да, но можно заморочиться — https://habrahabr.ru/post/305246/
              –3
              У меня это не сработало: https://habrahabr.ru/post/305246/#comment_10142540

              Видимо, когда-то так можно было, но теперь это сломали.
              В общем, сколько бы не говорили, что язык уже довольно рслый — но реально это штука пока еще сырая, хотя и выглядит перспективно.
                +2

                Так ведь компилятор говорит чего ему не хватает. И в мануале в примере это есть.

                  0
                  Вы напрасно минусуете. Пример из мануала предполагает, что если его закопипастить и попытаться собрать — должно собраться.
                  В данном случае это не так. В мануале написано, что нужны эти функции, эти функции используют #[feature] — а это фича только ночных сборок.
                  Сейчас я «застрял» на попытке собрать с #[no_std] в стабильном варианте. Согласно документации, либу оно должно собирать и в стабильном варианте тоже.

                  Если оно не может так делать в стабильном варианте — для продакшена не подходит.
                    +2
                    Пример из мануала предполагает, что если его закопипастить и попытаться собрать — должно собраться. В данном случае это не так.

                    Глава из секции "Nightly Rust", вполне логично что нужен ночной канал для использования того что там описано.


                    Сейчас я «застрял» на попытке собрать с #[no_std] в стабильном варианте.

                    Да, на стабильном канале этого сделать нельзя, возможность пока только для ночников. Когда-нибудь стабилизируют.


                    Если оно не может так делать в стабильном варианте — для продакшена не подходит.

                    Это может быть, хотя "продакшен" "продакшену" рознь — https://www.rust-lang.org/en-US/friends.html — многие из этих компаний пользуются именно ночными сборками.


                    С rustup вполне удобно зафиксировать какой-то ночник по дате и только когда сам захочешь — обновиться на более свежий ночник.

                      +1
                      вот тут: https://doc.rust-lang.org/book/using-rust-without-the-standard-library.html

                      Пишут Note, в котором заявляют, что stable должно уметь собирать либы без std. И это глава 5, а ночные сборки — это глава 6. Или я что-то неверно понял.
                      Так что должно. Но не собирает.
                        +1
                        Или я что-то неверно понял.

                        Верно. Это я пропустил момент когда обсуждение перешло с этой ссылки на эту.


                        В общем, библиотеку без std можно собрать на стабильной ржавчине уже давно, исполняемый файл — нет.


                        Стабилизация последнего не особо приоритетна, как я понимаю, потому что не так-то проста и на практике обычно не так уж и важна (по сравнению со сборкой библиотек).

                          0
                          Как? Как собрать библиотеку на стабильной ржавчине?
                          Пример из документации вываливает ошибки на все те же panic_fmt и eh_personality.
                            +2
                            $ cat src/lib.rs 
                            #![no_std]
                            pub fn plus_one(x: i32) -> i32 { x + 1 }
                            $ cat Cargo.toml 
                            [package]
                            name = "nostr"
                            version = "0.1.0"
                            
                            [lib]
                            # Если заменить rlib на staticlib или еще что - все печально :(
                            crate-type = ["rlib"]
                            $ c b
                               Compiling nostr v0.1.0 (file:///home/ozkriff/zoc/nostr)
                                Finished dev [unoptimized + debuginfo] target(s) in 0.10 secs
                            $ ls -lh target/debug/libnostr.rlib 
                            -rw-rw-r-- 2 ozkriff ozkriff 6,5K мар 29 19:45 target/debug/libnostr.rlib

                            Черт, и правда. rlib собрать можно, но толку от rlib без std никакого нет, потому что кроме как с ржавчиной такую библиотеку не используешь.


                            А вот staticlib или cdylib, которые как раз притворились бы сишными библиотеками, хотят panic_fmt и eh_personality :-( .

                              0
                              Ответили в итоге тут:

                              http://stackoverflow.com/questions/43097676/how-to-build-a-standard-linux-so-library-with-stable-rust-without-using-std

                              Получается, если хочешь создать либу, которую надо линковать с сишным кодом — ты ограничен только ночными сборками. И, по всей видимости, все нормальные люди этим моментом особо не заморачиваются. Так же, документация документирует ночные сборки, и дифференциации по фичам не делает.
                                +1
                                Ответили в итоге тут:

                                вроде, примерно то же что и я написал.


                                Получается, если хочешь создать либу, которую надо линковать с сишным кодом — ты ограничен только ночными сборками.

                                Если речь именно о "без std" то выходит так, да.


                                Так же, документация документирует ночные сборки, и дифференциации по фичам не делает.

                                Звучит как слишком мощная экстраполяция, так-то весь нестабильный функционал описывается в доках отдельно или с пометками. Просто конкретно этот момент с nostd такой хитрожопый вышел.


                                Зачем nostd тогда вообще стабилизировали я теперь не очень понимаю.

                                  +1
                                  Если речь именно о «без std» то выходит так, да

                                  Я тут путем эксперимента выяснил — оптимизатор выбрасывает std и core, если их не используешь де-факто (тип либ — cdylib). В том числе, при сборке в стабильной версии. Никакого #[no_std] писать вообще ненадо. При линковке такого .so ругается: undefined reference to `rust_begin_unwind', но это всего 1 ссылка, ее наверняка можно объявить в сишном коде, тем более что она вызываться не будет. Так что, на этот вопрос я пока просто забил.

                                  Мне пока надо понять концептуально — готов ли руст для продакшена. Выглядит для меня он весьма привлекательно, как замена си. Но вот использовать нестабильное апи — как-то стремно.
                                    +1
                                    Я тут путем эксперимента выяснил — оптимизатор выбрасывает std и core, если их не используешь де-факто (тип либ — cdylib).

                                    Конечно, без этой оптимизации статическое связывание теряло бы всякий смысл.


                                    Только надо учитывать, что какой-нибудь простой println косвенно использует значительную часть стандартной библиотеки.


                                    При линковке такого .so ругается: undefined reference to `rust_begin_unwind', но это всего 1 ссылка, ее наверняка можно объявить в сишном коде, тем более что она вызываться не будет.

                                    Можно в Cargo.toml прописать panic="abort", по идее это всю раскрутку (unwind) должно отключить: http://doc.crates.io/manifest.html#the-profile-sections

                                      +1

                                      Но в std и прочих частях останутся landing pads, в клиентском коде их, естественно не будет. Если хочется избавиться ещё и от них, то можно использовать xargo

              +3
              Если писать код, не используя фреймворки, то по объему получается примерно также, как если программировать на С.
              Например, если в примере из статьи убрать вызов usart2.print("..."), который использует collections::Vec, то вызов Rust-кода делает результат компиляции всего на сотню байт больше.
              А вот если подключить векторы, то это увеличит размер бинарника на целых 14кБ. Если взять пример посложнее, который использует стандартную библиотеку «на полную», то там будет 50кБ+.
              Но на мой взгляд, гораздо дешевле купить чип в котором будет больше памяти и при этом иметь возможность комфортно писать код. В том же STM32F103C8T6 64кБ flash памяти, чего обычно вполне хватает.
              Использовать другие библиотеки не пробовал.

            Only users with full accounts can post comments. Log in, please.